Skip to content

Commit d814801

Browse files
committed
Fix #5861 Amend YAML file without affecting its order
1 parent 988dead commit d814801

File tree

4 files changed

+85
-249
lines changed

4 files changed

+85
-249
lines changed

package.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,6 @@ library:
262262
- Stack.Unpack
263263
- Stack.Upgrade
264264
- Stack.Upload
265-
- Stack.YamlUpdate
266265
- System.Info.ShortPathName
267266
- System.Permissions
268267
- System.Process.Pager

src/Stack/ConfigCmd.hs

Lines changed: 85 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ module Stack.ConfigCmd
1717
,cfgCmdName) where
1818

1919
import Stack.Prelude
20-
import Data.Coerce (coerce)
2120
import qualified Data.Aeson.Key as Key
2221
import qualified Data.Aeson.KeyMap as KeyMap
23-
import Data.ByteString.Builder (byteString)
22+
import Data.Attoparsec.Text as P (Parser, parseOnly, skip, skipWhile,
23+
string, takeText, takeWhile)
2424
import qualified Data.Map.Merge.Strict as Map
2525
import qualified Data.Text as T
2626
import qualified Data.Yaml as Yaml
@@ -37,7 +37,6 @@ import Stack.Constants
3737
import Stack.Types.Config
3838
import Stack.Types.Resolver
3939
import System.Environment (getEnvironment)
40-
import Stack.YamlUpdate
4140

4241
data ConfigCmdSet
4342
= ConfigCmdSetResolver (Unresolved AbstractResolver)
@@ -73,29 +72,90 @@ cfgCmdSet cmd = do
7372
PCGlobalProject -> liftM (</> stackDotYaml) (getImplicitGlobalProjectDir conf)
7473
PCNoProject _extraDeps -> throwString "config command used when no project configuration available" -- maybe modify the ~/.stack/config.yaml file instead?
7574
CommandScopeGlobal -> return (configUserConfigPath conf)
76-
-- We don't need to worry about checking for a valid yaml here
77-
rawConfig <- mkRaw <$> liftIO (readFileUtf8 (toFilePath configFilePath))
78-
(config :: Yaml.Object) <- either throwM return (Yaml.decodeEither' . encodeUtf8 $ coerce rawConfig)
75+
rawConfig <- liftIO (readFileUtf8 (toFilePath configFilePath))
76+
config <- either throwM return (Yaml.decodeEither' $ encodeUtf8 rawConfig)
7977
newValue <- cfgCmdSetValue (parent configFilePath) cmd
80-
let cmdKey = cfgCmdSetOptionName cmd
81-
config' = KeyMap.insert (Key.fromText cmdKey) newValue config
82-
yamlKeys = Key.toText <$> KeyMap.keys config
83-
if config' == config
84-
then logInfo
85-
(fromString (toFilePath configFilePath) <>
86-
" already contained the intended configuration and remains \
87-
\unchanged.")
88-
else do
89-
let configLines = yamlLines rawConfig
90-
either
91-
throwM
92-
(\updated -> do
93-
let redressed = unmkRaw $ redress configLines updated
94-
writeBinaryFileAtomic configFilePath . byteString $ encodeUtf8 redressed
95-
96-
let file = fromString $ toFilePath configFilePath
97-
logInfo (file <> " has been updated."))
98-
(encodeInOrder configLines (coerce yamlKeys) (coerce cmdKey) config')
78+
let yamlLines = T.lines rawConfig
79+
cmdKey = cfgCmdSetOptionName cmd -- Text
80+
cmdKey' = Key.fromText cmdKey -- Data.Aeson.Key.Key
81+
newValue' = T.stripEnd $
82+
decodeUtf8With lenientDecode $ Yaml.encode newValue -- Text
83+
file = toFilePath configFilePath -- String
84+
file' = display $ T.pack file -- Utf8Builder
85+
newYamlLines <- case KeyMap.lookup cmdKey' config of
86+
Nothing -> do
87+
logInfo $ file' <> " has been extended."
88+
pure $ yamlLines <> [cmdKey <> ": " <> newValue']
89+
Just oldValue -> if oldValue == newValue
90+
then do
91+
logInfo $ file' <> " already contained the intended \
92+
\configuration and remains unchanged."
93+
pure yamlLines
94+
else switchLine file' cmdKey newValue' [] yamlLines
95+
liftIO $ writeFileUtf8 file (T.unlines newYamlLines)
96+
where
97+
switchLine file cmdKey _ searched [] = do
98+
logWarn $ display cmdKey <> " not found in YAML file " <> file <>
99+
" as a single line. Multi-line key:value formats are not supported."
100+
pure $ reverse searched
101+
switchLine file cmdKey newValue searched (oldLine:rest) =
102+
case parseOnly (parseLine cmdKey) oldLine of
103+
Left _ ->
104+
switchLine file cmdKey newValue (oldLine:searched) rest
105+
Right (kt, spaces1, spaces2, comment) -> do
106+
let newLine = renderKey cmdKey kt <> spaces1 <> ":" <>
107+
spaces2 <> newValue <> comment
108+
logInfo $ file <> " has been updated."
109+
pure $ reverse searched <> (newLine:rest)
110+
111+
-- This assumes that a top-level key will not be indented in the YAML file.
112+
parseLine :: Text -> Parser (KeyType, Text, Text, Text)
113+
parseLine key = do
114+
kt <- parseKey key
115+
spaces1 <- P.takeWhile (== ' ')
116+
skip (== ':')
117+
spaces2 <- P.takeWhile (== ' ')
118+
skipWhile (/= ' ')
119+
comment <- takeText
120+
pure (kt, spaces1, spaces2, comment)
121+
122+
-- If the key is, for example, install-ghc, this recognises install-ghc,
123+
-- 'install-ghc' or "install-ghc".
124+
parseKey :: Text -> Parser KeyType
125+
parseKey k = parsePlainKey k
126+
<|> parseSingleQuotedKey k
127+
<|> parseDoubleQuotedKey k
128+
129+
parsePlainKey :: Text -> Parser KeyType
130+
parsePlainKey key = do
131+
_ <- string key
132+
pure PlainKey
133+
134+
parseSingleQuotedKey :: Text -> Parser KeyType
135+
parseSingleQuotedKey = parseQuotedKey SingleQuotedKey '\''
136+
137+
parseDoubleQuotedKey :: Text -> Parser KeyType
138+
parseDoubleQuotedKey = parseQuotedKey DoubleQuotedKey '"'
139+
140+
parseQuotedKey :: KeyType -> Char -> Text -> Parser KeyType
141+
parseQuotedKey kt c key = do
142+
skip (==c)
143+
_ <- string key
144+
skip (==c)
145+
pure kt
146+
147+
renderKey :: Text -> KeyType -> Text
148+
renderKey key kt = case kt of
149+
PlainKey -> key
150+
SingleQuotedKey -> '\'' `T.cons` key `T.snoc` '\''
151+
DoubleQuotedKey -> '"' `T.cons` key `T.snoc` '"'
152+
153+
-- |Type representing types of representations of keys in YAML files.
154+
data KeyType
155+
= PlainKey -- ^ For example: install-ghc
156+
| SingleQuotedKey -- ^ For example: 'install-ghc'
157+
| DoubleQuotedKey -- ^ For example: "install-ghc"
158+
deriving (Eq, Show)
99159

100160
cfgCmdSetValue
101161
:: (HasConfig env, HasGHCVariant env)

src/Stack/YamlUpdate.hs

Lines changed: 0 additions & 222 deletions
This file was deleted.

stack.cabal

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,6 @@ library
212212
Stack.Unpack
213213
Stack.Upgrade
214214
Stack.Upload
215-
Stack.YamlUpdate
216215
System.Info.ShortPathName
217216
System.Permissions
218217
System.Process.Pager

0 commit comments

Comments
 (0)