From e5eb53adceaa5ba6e0a9e3e897e5b8dde053a703 Mon Sep 17 00:00:00 2001 From: webdevred <148627186+webdevred@users.noreply.github.com> Date: Sat, 18 Jul 2026 13:34:31 +0200 Subject: [PATCH 1/9] Add spec for newline detection --- test/Core/NewlineSpec.hs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 test/Core/NewlineSpec.hs diff --git a/test/Core/NewlineSpec.hs b/test/Core/NewlineSpec.hs new file mode 100644 index 00000000..59bb68fd --- /dev/null +++ b/test/Core/NewlineSpec.hs @@ -0,0 +1,17 @@ +module Core.NewlineSpec (spec) where + +import JbeamEdit.Core.Newline (detectNewline) +import System.IO (Newline (..)) +import Test.Hspec + +spec :: Spec +spec = + describe "detectNewline" $ do + it "detects LF for unix line endings" $ + detectNewline "a\nb\n" `shouldBe` LF + it "detects CRLF for windows line endings" $ + detectNewline "a\r\nb\r\n" `shouldBe` CRLF + it "detects CRLF when line endings are mixed" $ + detectNewline "a\r\nb\n" `shouldBe` CRLF + it "defaults to LF when there is no line ending" $ + detectNewline "abc" `shouldBe` LF From 37078edbed219c0ddd7f6ce574f5e9f5977e9309 Mon Sep 17 00:00:00 2001 From: webdevred <148627186+webdevred@users.noreply.github.com> Date: Sat, 8 Aug 2026 23:49:15 +0200 Subject: [PATCH 2/9] Implemented newline detection support --- jbeam-edit.cabal | 2 ++ src/JbeamEdit/Core/Newline.hs | 14 ++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 src/JbeamEdit/Core/Newline.hs diff --git a/jbeam-edit.cabal b/jbeam-edit.cabal index a60dfd62..2bcb3ec6 100644 --- a/jbeam-edit.cabal +++ b/jbeam-edit.cabal @@ -63,6 +63,7 @@ flag windows-example-paths library exposed-modules: + JbeamEdit.Core.Newline JbeamEdit.Core.Node JbeamEdit.Core.NodeCursor JbeamEdit.Core.NodePath @@ -262,6 +263,7 @@ test-suite jbeam-edit-test build-tool-depends: hspec-discover:hspec-discover hs-source-dirs: test other-modules: + Core.NewlineSpec Core.NodeCursorSpec Core.NodePathSpec Core.NodeSpec diff --git a/src/JbeamEdit/Core/Newline.hs b/src/JbeamEdit/Core/Newline.hs new file mode 100644 index 00000000..6071d102 --- /dev/null +++ b/src/JbeamEdit/Core/Newline.hs @@ -0,0 +1,14 @@ +module JbeamEdit.Core.Newline (detectNewline) where + +import Data.ByteString.Lazy as LBS +import Data.ByteString.Lazy.Char8 as LBS8 (elemIndex) +import System.IO (Newline (..)) + +detectNewline :: LBS.ByteString -> Newline +detectNewline content = + case LBS8.elemIndex '\r' content of + Nothing -> LF + Just idx -> + case LBS.index content (idx + 1) of + 10 -> CRLF -- '\n' + _ -> detectNewline (LBS.drop (idx + 1) content) From 44349fb13ec78f90bb26bef565dfb70d585a160e Mon Sep 17 00:00:00 2001 From: webdevred <148627186+webdevred@users.noreply.github.com> Date: Sat, 8 Aug 2026 23:51:14 +0200 Subject: [PATCH 3/9] Added GHC 9.8.4 to ghc list --- jbeam-edit.cabal | 2 +- package.yaml | 2 +- src-extra/transformation/JbeamEdit/Transformation.hs | 2 +- .../transformation/JbeamEdit/Transformation/BeamValidation.hs | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/jbeam-edit.cabal b/jbeam-edit.cabal index 2bcb3ec6..aaeb4bd4 100644 --- a/jbeam-edit.cabal +++ b/jbeam-edit.cabal @@ -11,7 +11,7 @@ license-file: LICENSE copyright: 2025 webdevred maintainer: example@example.com author: webdevred -tested-with: ghc ==9.10.3 ghc ==9.14.1 +tested-with: ghc ==9.8.4 ghc ==9.10.3 ghc ==9.14.1 homepage: https://github.com/webdevred/jbeam-edit#readme bug-reports: https://github.com/webdevred/jbeam-edit/issues synopsis: diff --git a/package.yaml b/package.yaml index e07ccea6..0ea56827 100644 --- a/package.yaml +++ b/package.yaml @@ -25,7 +25,7 @@ description: >- and can automatically rename nodes and update references. Custom formatting rules are supported via JBFL (JBeam Formatting Language). See the README for usage instructions and examples: https://github.com/webdevred/jbeam-edit#readme -tested-with: [GHC == 9.10.3, GHC == 9.14.1] +tested-with: [GHC == 9.8.4, GHC == 9.10.3, GHC == 9.14.1] dependencies: - base >= 4.19 diff --git a/src-extra/transformation/JbeamEdit/Transformation.hs b/src-extra/transformation/JbeamEdit/Transformation.hs index 8e043502..34681f0e 100644 --- a/src-extra/transformation/JbeamEdit/Transformation.hs +++ b/src-extra/transformation/JbeamEdit/Transformation.hs @@ -5,7 +5,7 @@ import Data.Bifunctor (first) import Data.Bool (bool) import Data.Foldable.Extra (notNull) import Data.Function (on) -import Data.List (partition) +import Data.List (foldl', partition) import Data.List.NonEmpty (NonEmpty) import Data.List.NonEmpty qualified as NE import Data.Map (Map) diff --git a/src-extra/transformation/JbeamEdit/Transformation/BeamValidation.hs b/src-extra/transformation/JbeamEdit/Transformation/BeamValidation.hs index f0c3aee3..e4e34df3 100644 --- a/src-extra/transformation/JbeamEdit/Transformation/BeamValidation.hs +++ b/src-extra/transformation/JbeamEdit/Transformation/BeamValidation.hs @@ -7,6 +7,7 @@ module JbeamEdit.Transformation.BeamValidation ( ) where import Control.Monad (forM_, unless) +import Data.List (foldl') import Data.List.NonEmpty (toList) import Data.Map qualified as M import Data.Set (Set) From 5b28fb07819041c775998c91a80300c10c710417 Mon Sep 17 00:00:00 2001 From: webdevred <148627186+webdevred@users.noreply.github.com> Date: Sat, 8 Aug 2026 23:55:47 +0200 Subject: [PATCH 4/9] Ran fourmolu --- src/JbeamEdit/Core/Newline.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/JbeamEdit/Core/Newline.hs b/src/JbeamEdit/Core/Newline.hs index 6071d102..15c21338 100644 --- a/src/JbeamEdit/Core/Newline.hs +++ b/src/JbeamEdit/Core/Newline.hs @@ -10,5 +10,5 @@ detectNewline content = Nothing -> LF Just idx -> case LBS.index content (idx + 1) of - 10 -> CRLF -- '\n' - _ -> detectNewline (LBS.drop (idx + 1) content) + 10 -> CRLF -- '\n' + _ -> detectNewline (LBS.drop (idx + 1) content) From 96637779f800314022305b6eb755806f2250f844 Mon Sep 17 00:00:00 2001 From: webdevred <148627186+webdevred@users.noreply.github.com> Date: Sun, 9 Aug 2026 00:04:52 +0200 Subject: [PATCH 5/9] Fixed impartial function call --- src/JbeamEdit/Core/Newline.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/JbeamEdit/Core/Newline.hs b/src/JbeamEdit/Core/Newline.hs index 15c21338..9c402f4d 100644 --- a/src/JbeamEdit/Core/Newline.hs +++ b/src/JbeamEdit/Core/Newline.hs @@ -9,6 +9,6 @@ detectNewline content = case LBS8.elemIndex '\r' content of Nothing -> LF Just idx -> - case LBS.index content (idx + 1) of - 10 -> CRLF -- '\n' + case LBS.uncons (LBS.drop (idx+1) content) of + Just (10, _) -> CRLF -- '\n' _ -> detectNewline (LBS.drop (idx + 1) content) From 76038e7da8a2619f6a3c0f61b9228fd344d7b231 Mon Sep 17 00:00:00 2001 From: webdevred <148627186+webdevred@users.noreply.github.com> Date: Sun, 9 Aug 2026 00:36:33 +0200 Subject: [PATCH 6/9] Wire the new newline detection into the IO code --- exe/jbeam-edit/Main.hs | 9 +++++---- src/JbeamEdit/Formatting.hs | 19 ++++++++++++------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/exe/jbeam-edit/Main.hs b/exe/jbeam-edit/Main.hs index c65c9e25..b19b15b2 100644 --- a/exe/jbeam-edit/Main.hs +++ b/exe/jbeam-edit/Main.hs @@ -12,6 +12,7 @@ import JbeamEdit.IOUtils import JbeamEdit.Parsing.Jbeam (parseNodes) import System.Directory.OsPath import System.Environment (getArgs) +import System.IO (Newline) import System.OsPath #ifdef ENABLE_WINDOWS_NEWLINES @@ -49,15 +50,15 @@ editFile opts = do createBackupFile filename opts contents <- tryReadFile [] filename case contents >>= parseNodes of - Right ns -> processNodes opts filename ns formattingConfig + Right ns -> processNodes (detectNewline contents) opts filename ns formattingConfig Left err -> putErrorLine err Nothing -> putErrorLine "missing arg filename" -processNodes :: Options -> OsPath -> Node -> RuleSet -> IO () -processNodes opts outFile nodes formattingConfig = do +processNodes :: Newline -> Options -> OsPath -> Node -> RuleSet -> IO () +processNodes newline opts outFile nodes formattingConfig = do transformedNode <- applyTransform formattingConfig opts nodes case transformedNode of - Right transformedNode' -> formatNodeAndWrite formattingConfig outFile transformedNode' + Right transformedNode' -> formatNodeAndWrite newline formattingConfig outFile transformedNode' Left err -> putErrorLine err applyTransform :: RuleSet -> Options -> Node -> IO (Either Text Node) diff --git a/src/JbeamEdit/Formatting.hs b/src/JbeamEdit/Formatting.hs index 320f0a06..b5a51c1c 100644 --- a/src/JbeamEdit/Formatting.hs +++ b/src/JbeamEdit/Formatting.hs @@ -10,8 +10,9 @@ module JbeamEdit.Formatting ( emptyState, ) where +import System.IO import Data.Bool (bool) -import Data.ByteString.Lazy qualified as LBS (fromStrict) +import Data.ByteString.Lazy qualified as LBS (fromStrict,hPutStr) import Data.Char (isSpace) import Data.Foldable.Extra (notNull) import Data.Map.Strict (Map) @@ -51,7 +52,7 @@ import JbeamEdit.Formatting.Rules ( ) import JbeamEdit.Formatting.Rules.ComplexNewLine qualified as CNL import JbeamEdit.Formatting.Rules.TrailingComma qualified as TC -import System.File.OsPath qualified as OS (writeFile) +import System.File.OsPath qualified as OS import System.OsPath (OsPath) data FormattingState = FormattingState @@ -468,13 +469,17 @@ replaceNewlines = id #endif formatNodeAndWrite - :: RuleSet + :: Newline + -> RuleSet -> OsPath -> Node -> IO () -formatNodeAndWrite rs outFile = - OS.writeFile outFile - . LBS.fromStrict +formatNodeAndWrite newline rs outFile node = do + handle <- OS.openFile outFile WriteMode + hSetNewlineMode handle (NewlineMode LF newline) + LBS.hPutStr handle . + LBS.fromStrict . encodeUtf8 . replaceNewlines - . formatNode rs + $ formatNode rs node + hClose handle From de26d4b03d4eb78aad054235c0ea160d397cc385 Mon Sep 17 00:00:00 2001 From: webdevred <148627186+webdevred@users.noreply.github.com> Date: Sun, 9 Aug 2026 00:45:22 +0200 Subject: [PATCH 7/9] Use the the newline detection in cross file update code --- .../transformation/JbeamEdit/Transformation.hs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src-extra/transformation/JbeamEdit/Transformation.hs b/src-extra/transformation/JbeamEdit/Transformation.hs index 34681f0e..081e5142 100644 --- a/src-extra/transformation/JbeamEdit/Transformation.hs +++ b/src-extra/transformation/JbeamEdit/Transformation.hs @@ -24,6 +24,7 @@ import Data.Traversable (mapAccumL) import Data.Vector (Vector, (!), (!?), (//)) import Data.Vector qualified as V import GHC.IsList +import JbeamEdit.Core.Newline import JbeamEdit.Core.Node import JbeamEdit.Core.NodeCursor (newCursor) import JbeamEdit.Core.NodeCursor qualified as NC @@ -492,12 +493,15 @@ filterJbeamFiles excludedFilenames = filter go updateOtherFiles :: RuleSet -> UpdateNamesMap -> OsPath -> IO () updateOtherFiles formattingConfig updatedNames filepath = do contents <- tryReadFile [] filepath - case contents >>= parseNodes of - Right node -> - let node' = findAndUpdateTextInNode updatedNames newCursor node - in when - (node /= node') - (formatNodeAndWrite formattingConfig filepath node') + case contents of + Right contents' -> + case parseNodes contents' of + Right node -> + let node' = findAndUpdateTextInNode updatedNames newCursor node + in when + (node /= node') + (formatNodeAndWrite (detectNewline contents') formattingConfig filepath node') + Left err -> putErrorLine err Left err -> putErrorLine err transform From 1e38334543e9847d0bf1bd04150cc83c0e5b9464 Mon Sep 17 00:00:00 2001 From: webdevred <148627186+webdevred@users.noreply.github.com> Date: Sun, 9 Aug 2026 00:47:10 +0200 Subject: [PATCH 8/9] Ran fourmolu --- src/JbeamEdit/Core/Newline.hs | 2 +- src/JbeamEdit/Formatting.hs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/JbeamEdit/Core/Newline.hs b/src/JbeamEdit/Core/Newline.hs index 9c402f4d..fb11267f 100644 --- a/src/JbeamEdit/Core/Newline.hs +++ b/src/JbeamEdit/Core/Newline.hs @@ -9,6 +9,6 @@ detectNewline content = case LBS8.elemIndex '\r' content of Nothing -> LF Just idx -> - case LBS.uncons (LBS.drop (idx+1) content) of + case LBS.uncons (LBS.drop (idx + 1) content) of Just (10, _) -> CRLF -- '\n' _ -> detectNewline (LBS.drop (idx + 1) content) diff --git a/src/JbeamEdit/Formatting.hs b/src/JbeamEdit/Formatting.hs index b5a51c1c..42151fe1 100644 --- a/src/JbeamEdit/Formatting.hs +++ b/src/JbeamEdit/Formatting.hs @@ -10,9 +10,8 @@ module JbeamEdit.Formatting ( emptyState, ) where -import System.IO import Data.Bool (bool) -import Data.ByteString.Lazy qualified as LBS (fromStrict,hPutStr) +import Data.ByteString.Lazy qualified as LBS (fromStrict, hPutStr) import Data.Char (isSpace) import Data.Foldable.Extra (notNull) import Data.Map.Strict (Map) @@ -53,6 +52,7 @@ import JbeamEdit.Formatting.Rules ( import JbeamEdit.Formatting.Rules.ComplexNewLine qualified as CNL import JbeamEdit.Formatting.Rules.TrailingComma qualified as TC import System.File.OsPath qualified as OS +import System.IO import System.OsPath (OsPath) data FormattingState = FormattingState @@ -477,8 +477,8 @@ formatNodeAndWrite formatNodeAndWrite newline rs outFile node = do handle <- OS.openFile outFile WriteMode hSetNewlineMode handle (NewlineMode LF newline) - LBS.hPutStr handle . - LBS.fromStrict + LBS.hPutStr handle + . LBS.fromStrict . encodeUtf8 . replaceNewlines $ formatNode rs node From da50ecd0526d5b006fdfb20f828ae4d9db184b3c Mon Sep 17 00:00:00 2001 From: webdevred <148627186+webdevred@users.noreply.github.com> Date: Sun, 9 Aug 2026 00:53:08 +0200 Subject: [PATCH 9/9] Fix --- exe/jbeam-edit/Main.hs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/exe/jbeam-edit/Main.hs b/exe/jbeam-edit/Main.hs index b19b15b2..e131583c 100644 --- a/exe/jbeam-edit/Main.hs +++ b/exe/jbeam-edit/Main.hs @@ -5,6 +5,7 @@ module Main ( import CommandLineOptions import Control.Monad (when) import Data.Text (Text) +import JbeamEdit.Core.Newline import JbeamEdit.Core.Node (Node) import JbeamEdit.Formatting (RuleSet, formatNodeAndWrite) import JbeamEdit.Formatting.Config @@ -49,8 +50,11 @@ editFile opts = do Just filename -> do createBackupFile filename opts contents <- tryReadFile [] filename - case contents >>= parseNodes of - Right ns -> processNodes (detectNewline contents) opts filename ns formattingConfig + case contents of + Right contents' -> + case parseNodes contents' of + Right ns -> processNodes (detectNewline contents') opts filename ns formattingConfig + Left err -> putErrorLine err Left err -> putErrorLine err Nothing -> putErrorLine "missing arg filename"