Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,3 +5,4 @@
!/.lua-format
!/.githooks/
/output/
/dist/
8 changes: 8 additions & 0 deletions changelog.d/20260713_120000_yura_group.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
### Added

- `Effect.Console`: `group`, `groupCollapsed`, `groupEnd`, and `grouped`, plus
their `MonadEffect` counterparts in `Effect.Class.Console` — upstream
purescript-console v6.1.0 parity (#268). The Lua FFI keeps an indentation
level: a group prints its label and indents subsequent console output by
two spaces until the matching `groupEnd`; `groupCollapsed` behaves like
`group`, since a terminal cannot collapse anything.
17 changes: 17 additions & 0 deletions src/Effect/Class/Console.purs
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
module Effect.Class.Console where

import Control.Bind (discard, bind, pure)
import Data.Function ((<<<))
import Data.Show (class Show)
import Data.Unit (Unit)
Expand DownExpand Up@@ -47,3 +48,19 @@ timeEnd = liftEffect <<< EffConsole.timeEnd

clear :: forall m. MonadEffect m => m Unit
clear = liftEffect EffConsole.clear

group :: forall m. MonadEffect m => String -> m Unit
group = liftEffect <<< EffConsole.group

groupCollapsed :: forall m. MonadEffect m => String -> m Unit
groupCollapsed = liftEffect <<< EffConsole.groupCollapsed

groupEnd :: forall m. MonadEffect m => m Unit
groupEnd = liftEffect EffConsole.groupEnd

grouped :: forall m a. MonadEffect m => String -> m a -> m a
grouped name inner = do
group name
result <- inner
groupEnd
pure result
16 changes: 16 additions & 0 deletions src/Effect/Console.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,3 +49,19 @@ export const timeEnd = function (s) {
export const clear = function () {
console.clear();
};

export const group = function (s) {
return function () {
console.group(s);
};
};

export const groupCollapsed = function (s) {
return function () {
console.groupCollapsed(s);
};
};

export const groupEnd = function () {
console.groupEnd();
};
30 changes: 24 additions & 6 deletions src/Effect/Console.lua
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,29 @@
-- Grouping (group / groupCollapsed / groupEnd) approximates the JS console
-- semantics in a terminal: group prints its label and indents every following
-- message by two more spaces until the matching groupEnd. A terminal cannot
-- collapse a group, so groupCollapsed behaves exactly like group.
local indentLevel = 0

local function indented(s) return string.rep(" ", indentLevel) .. tostring(s) end

local function openGroup(label)
return function()
print(indented(label))
indentLevel = indentLevel + 1
end
end

return {
log = (function(s) return function() print(s) end end),
warn = (function(s) return function() io.stderr:write(tostring(s) .. "\n") end end),
error = (function(s) return function() io.stderr:write(tostring(s) .. "\n") end end),
info = (function(s) return function() print(s) end end),
debug = (function(s) return function() print(s) end end),
log = (function(s) return function() print(indented(s)) end end),
warn = (function(s) return function() io.stderr:write(indented(s) .. "\n") end end),
error = (function(s) return function() io.stderr:write(indented(s) .. "\n") end end),
info = (function(s) return function() print(indented(s)) end end),
debug = (function(s) return function() print(indented(s)) end end),
time = (function(_s) return function() error("Sorry, console timers aren't implemented yet") end end),
timeLog = (function(_s) return function() error("Sorry, console timers aren't implemented yet") end end),
timeEnd = (function(_s) return function() error("Sorry, console timers aren't implemented yet") end end),
clear = (function() io.write("\027[H\027[2J") end)
clear = (function() io.write("\027[H\027[2J") end),
group = (openGroup),
groupCollapsed = (openGroup),
groupEnd = (function() if indentLevel > 0 then indentLevel = indentLevel - 1 end end)
}
20 changes: 20 additions & 0 deletions src/Effect/Console.purs
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
module Effect.Console where

import Control.Bind (discard, bind, pure)
import Effect (Effect)

import Data.Show (class Show, show)
Expand DownExpand Up@@ -66,3 +67,22 @@ foreign import timeEnd :: String -> Effect Unit

-- | Clears the console
foreign import clear :: Effect Unit

-- | Creates a new inline group in the console. This indents following console
-- | messages by an additional level, until `groupEnd` is called.
foreign import group :: String -> Effect Unit

-- | Same as `group`, but groups are collapsed by default.
foreign import groupCollapsed :: String -> Effect Unit

-- | Exits the current inline group in the console.
foreign import groupEnd :: Effect Unit

-- | Perform an effect within the context of an inline group in the console.
-- | Calls `group` and `groupEnd` before and after the effect, respectively.
grouped :: forall a. String -> Effect a -> Effect a
grouped name inner = do
group name
result <- inner
groupEnd
pure result
34 changes: 34 additions & 0 deletions test/regression/console.lua
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,10 @@
-- #76 error :: String -> Effect Unit maps to console.error -> stderr in Node;
-- the Lua FFI routed it through print (stdout). Now io.stderr:write.
-- #77 warn -> console.warn -> stderr, likewise (was print/stdout).
-- #268 group/groupCollapsed/groupEnd were missing from the fork. They keep
-- an indentation level: group prints its label and indents subsequent
-- messages by two spaces per open group; groupCollapsed = group (a
-- terminal cannot collapse); groupEnd at level zero is a no-op.
-- log/info/debug correctly stay on stdout (console.log/info/debug all do).
--
-- The FFI reads `print` / `io.stderr` as globals at call time, so we swap them
Expand DownExpand Up@@ -50,5 +54,35 @@ do
check("log does not write to stderr", #err == 0, "stderr=" .. table.concat(err))
end

do
local out, err = capture(function()
C.group("outer")()
C.log("one")()
C.groupCollapsed("inner")()
C.warn("two")()
C.groupEnd()
C.log("three")()
C.groupEnd()
C.log("four")()
end)
check("group prints its label at the current indent", out[1] == "outer", "out[1]=" .. tostring(out[1]))
check("log inside a group is indented by two spaces", out[2] == " one", "out[2]=" .. tostring(out[2]))
check("groupCollapsed prints its label like group", out[3] == " inner", "out[3]=" .. tostring(out[3]))
check("warn inside nested groups is indented on stderr", err[1] == " two\n", "err[1]=" .. tostring(err[1]))
check("groupEnd closes the innermost group", out[4] == " three", "out[4]=" .. tostring(out[4]))
check("groupEnd unwinds back to no indent", out[5] == "four", "out[5]=" .. tostring(out[5]))
check("grouping writes nothing extra", #out == 5 and #err == 1, "#out=" .. #out .. " #err=" .. #err)
end

do
local out = capture(function()
C.groupEnd() -- no open group: must be a no-op, not an indent debt
C.group("g")()
C.log("m")()
C.groupEnd()
end)
check("groupEnd without an open group is a no-op", out[1] == "g" and out[2] == " m", "out=" .. table.concat(out, ","))
end

if failures > 0 then error(failures .. " regression check(s) failed") end
print("purescript-lua-console: all FFI regression checks passed")