Skip to content

Commit ea032ce

Browse files
cdsmithmergify[bot]jneira
authored
Consider all root paths when suggesting module name change. (#2195)
If there are source root dirs nested inside each other, a module might have more than one possible name, depending on where it's intended to be imported from. In this case, a rename should not be suggested unless the module name doesn't match any possible correct name. When suggesting a name, the shortest name should be suggested, since that's more likely to be the intended one. Fixes #1903 Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Javier Neira <[email protected]>
1 parent ec53fcb commit ea032ce

File tree

5 files changed

+42
-17
lines changed

5 files changed

+42
-17
lines changed

plugins/hls-module-name-plugin/src/Ide/Plugin/ModuleName.hs

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import Control.Monad.Trans.Maybe
2121
import Data.Aeson (Value (Null), toJSON)
2222
import Data.Char (isLower)
2323
import qualified Data.HashMap.Strict as HashMap
24-
import Data.List (find, intercalate, isPrefixOf)
24+
import Data.List (intercalate, isPrefixOf, minimumBy)
2525
import Data.Maybe (maybeToList)
2626
import Data.String (IsString)
2727
import qualified Data.Text as T
@@ -44,6 +44,7 @@ import Language.LSP.VFS (virtualFileText)
4444
import System.Directory (canonicalizePath)
4545
import System.FilePath (dropExtension, splitDirectories,
4646
takeFileName)
47+
import Data.Ord (comparing)
4748

4849
-- |Plugin descriptor
4950
descriptor :: PluginId -> PluginDescriptor IdeState
@@ -97,36 +98,40 @@ action state uri =
9798
contents <- lift . getVirtualFile $ toNormalizedUri uri
9899
let emptyModule = maybe True (T.null . T.strip . virtualFileText) contents
99100

100-
correctName <- MaybeT . liftIO $ traceAs "correctName" <$> pathModuleName state nfp fp
101+
correctNames <- liftIO $ traceAs "correctNames" <$> pathModuleNames state nfp fp
102+
let bestName = minimumBy (comparing T.length) correctNames
101103

102104
statedNameMaybe <- liftIO $ traceAs "statedName" <$> codeModuleName state nfp
103105
case statedNameMaybe of
104106
Just (nameRange, statedName)
105-
| correctName /= statedName ->
106-
pure $ Replace uri nameRange ("Set module name to " <> correctName) correctName
107+
| statedName `notElem` correctNames ->
108+
pure $ Replace uri nameRange ("Set module name to " <> bestName) bestName
107109
Nothing
108110
| emptyModule ->
109-
let code = "module " <> correctName <> " where\n"
111+
let code = "module " <> bestName <> " where\n"
110112
in pure $ Replace uri (Range (Position 0 0) (Position 0 0)) code code
111113
_ -> MaybeT $ pure Nothing
112114

113-
-- | The module name, as derived by the position of the module in its source directory
114-
pathModuleName :: IdeState -> NormalizedFilePath -> String -> IO (Maybe T.Text)
115-
pathModuleName state normFilePath filePath
116-
| isLower . head $ takeFileName filePath = return $ Just "Main"
115+
-- | Possible module names, as derived by the position of the module in the
116+
-- source directories. There may be more than one possible name, if the source
117+
-- directories are nested inside each other.
118+
pathModuleNames :: IdeState -> NormalizedFilePath -> String -> IO [T.Text]
119+
pathModuleNames state normFilePath filePath
120+
| isLower . head $ takeFileName filePath = return ["Main"]
117121
| otherwise = do
118122
session <- runAction "ModuleName.ghcSession" state $ use_ GhcSession normFilePath
119123
srcPaths <- evalGhcEnv (hscEnvWithImportPaths session) $ importPaths <$> getSessionDynFlags
120124
paths <- mapM canonicalizePath srcPaths
121125
mdlPath <- canonicalizePath filePath
122-
pure $ do
123-
prefix <- find (`isPrefixOf` mdlPath) paths
124-
pure
125-
. T.pack
126-
. intercalate "."
127-
. splitDirectories
128-
. drop (length prefix + 1)
129-
$ dropExtension mdlPath
126+
let prefixes = filter (`isPrefixOf` mdlPath) paths
127+
pure (map (moduleNameFrom mdlPath) prefixes)
128+
where
129+
moduleNameFrom mdlPath prefix =
130+
T.pack
131+
. intercalate "."
132+
. splitDirectories
133+
. drop (length prefix + 1)
134+
$ dropExtension mdlPath
130135

131136
-- | The module name, as stated in the module
132137
codeModuleName :: IdeState -> NormalizedFilePath -> IO (Maybe (Range, T.Text))

plugins/hls-module-name-plugin/test/Main.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ tests =
3232
[CodeLens { _command = Just c }] <- getCodeLenses doc
3333
executeCommand c
3434
void $ skipManyTill anyMessage (message SWorkspaceApplyEdit)
35+
36+
, goldenWithModuleName "Fix wrong module name in nested directory" "subdir/TWrongModuleName" $ \doc -> do
37+
[CodeLens { _command = Just c }] <- getCodeLenses doc
38+
executeCommand c
39+
void $ skipManyTill anyMessage (message SWorkspaceApplyEdit)
3540
]
3641

3742
goldenWithModuleName :: TestName -> FilePath -> (TextDocumentIdentifier -> Session ()) -> TestTree
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
cradle:
22
direct:
33
arguments:
4+
- "-isubdir"
45
- "TEmptyModule"
56
- "TWrongModuleName"
67
- "mainlike"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module TWrongModuleName
2+
( x
3+
)
4+
where
5+
6+
x :: Integer
7+
x = 11
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module BadName
2+
( x
3+
)
4+
where
5+
6+
x :: Integer
7+
x = 11

0 commit comments

Comments
 (0)