1
+ {-# LANGUAGE LambdaCase #-}
1
2
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2
3
{-# LANGUAGE DeriveGeneric #-}
3
4
{-# LANGUAGE RecordWildCards #-}
@@ -8,7 +9,7 @@ module Language.LSP.Types.Uri
8
9
, NormalizedUri (.. )
9
10
, toNormalizedUri
10
11
, fromNormalizedUri
11
- , NormalizedFilePath ( .. )
12
+ , NormalizedFilePath
12
13
, toNormalizedFilePath
13
14
, fromNormalizedFilePath
14
15
, normalizedFilePathToUri
@@ -23,16 +24,20 @@ import Control.DeepSeq
23
24
import qualified Data.Aeson as A
24
25
import Data.Binary (Binary , Get , put , get )
25
26
import Data.Hashable
27
+ import qualified Data.HashMap.Strict as HM
28
+ import Data.IORef (atomicModifyIORef' , newIORef )
26
29
import Data.List (stripPrefix )
27
30
import Data.String (IsString , fromString )
28
31
import Data.Text (Text )
29
32
import qualified Data.Text as T
33
+ import Data.Tuple (swap )
30
34
import GHC.Generics
31
35
import Network.URI hiding (authority )
32
36
import qualified System.FilePath as FP
33
37
import qualified System.FilePath.Posix as FPP
34
38
import qualified System.FilePath.Windows as FPW
35
39
import qualified System.Info
40
+ import System.IO.Unsafe (unsafePerformIO )
36
41
37
42
newtype Uri = Uri { getUri :: Text }
38
43
deriving (Eq ,Ord ,Read ,Show ,Generic ,A.FromJSON ,A.ToJSON ,Hashable ,A.ToJSONKey ,A.FromJSONKey )
@@ -165,7 +170,11 @@ instance Binary NormalizedFilePath where
165
170
get = do
166
171
v <- Data.Binary. get :: Get FilePath
167
172
let nuri = internalNormalizedFilePathToUri v
168
- return (NormalizedFilePath nuri v)
173
+ return (normalizedFilePath nuri v)
174
+
175
+ -- | A smart constructor that performs UTF-8 encoding and hash consing
176
+ normalizedFilePath :: NormalizedUri -> FilePath -> NormalizedFilePath
177
+ normalizedFilePath nuri nfp = intern $ NormalizedFilePath nuri nfp
169
178
170
179
-- | Internal helper that takes a file path that is assumed to
171
180
-- already be normalized to a URI. It is up to the caller
@@ -188,7 +197,7 @@ instance IsString NormalizedFilePath where
188
197
fromString = toNormalizedFilePath
189
198
190
199
toNormalizedFilePath :: FilePath -> NormalizedFilePath
191
- toNormalizedFilePath fp = NormalizedFilePath nuri nfp
200
+ toNormalizedFilePath fp = normalizedFilePath nuri nfp
192
201
where
193
202
nfp = FP. normalise fp
194
203
nuri = internalNormalizedFilePathToUri nfp
@@ -200,5 +209,20 @@ normalizedFilePathToUri :: NormalizedFilePath -> NormalizedUri
200
209
normalizedFilePathToUri (NormalizedFilePath uri _) = uri
201
210
202
211
uriToNormalizedFilePath :: NormalizedUri -> Maybe NormalizedFilePath
203
- uriToNormalizedFilePath nuri = fmap (NormalizedFilePath nuri) mbFilePath
212
+ uriToNormalizedFilePath nuri = fmap (normalizedFilePath nuri) mbFilePath
204
213
where mbFilePath = platformAwareUriToFilePath System.Info. os (fromNormalizedUri nuri)
214
+
215
+ ---------------------------------------------------------------------------
216
+ -- Unsafe hashcons of NFP
217
+ internIO :: (Eq a , Hashable a ) => IO (a -> IO a )
218
+ internIO = do
219
+ tableRef <- newIORef mempty
220
+ let f x = atomicModifyIORef' tableRef $ swap . flip HM. alterF x (\ case
221
+ Just res -> (res, Just res)
222
+ Nothing -> (x, Just x)
223
+ )
224
+ return f
225
+
226
+ {-# NOINLINE intern #-}
227
+ intern :: NormalizedFilePath -> NormalizedFilePath
228
+ intern = let f = unsafePerformIO internIO in \ x -> unsafePerformIO (f x)
0 commit comments