Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
9cea815
Incomplete state of Debug instance visualization
meipp Sep 19, 2025
5bcdba9
expandTerm: RefWrap and NewtypeWrap cases
alt-romes Oct 31, 2025
ea1f006
refactor: Don't do work to expand boring ty twice
alt-romes Oct 31, 2025
6e7ebeb
refactor: Move isBoringTy to Debugger.Stopped.Variables
alt-romes Oct 31, 2025
d6e8990
Bump version to 0.10.0
alt-romes Nov 3, 2025
97323a9
feat: Custom Debug Instances
alt-romes Nov 3, 2025
788d2c3
fix: Only add special target if there are no targets
alt-romes Nov 7, 2025
7f62324
Flatten VarFields into newtype
alt-romes Nov 7, 2025
433c83f
fix: Re-use cache when recursing into path fragments
alt-romes Nov 10, 2025
04ecc12
Add test for #47 with custom instance
alt-romes Nov 10, 2025
bb60560
fix: Use propper logger in findDebugViewInstance
alt-romes Nov 10, 2025
0889a25
Delete JSON instances for adapter-debugger Messages
alt-romes Nov 10, 2025
d4a4e98
Load in-memory haskell-debugger-view when it is not in closure
alt-romes Nov 10, 2025
e3c0e48
Use file-embed to embed class in hdb
alt-romes Nov 10, 2025
50e184d
Robust loading of built-in debug view classes
alt-romes Nov 10, 2025
d0c874d
refactor: Move things from .Monad module to better ones
alt-romes Nov 11, 2025
58ea444
refactor: Move some builtin things from Monad
alt-romes Nov 11, 2025
8b5a431
Accept tests with better variable expansion
alt-romes Nov 11, 2025
eb5e4ca
refactor: Simplify runDebugger
alt-romes Nov 11, 2025
45550ca
fixes: Don't block on handle contents on exception if there's no data
alt-romes Nov 12, 2025
37fa2ff
fix: Qualify the unit-id of the GHC.Debugger.View.Class import
alt-romes Nov 12, 2025
17db6a2
fix: Use IIModule when the module is in in-memory home unit
alt-romes Nov 12, 2025
c406372
hs-dbg-view: Guard all dependencies behind flags
alt-romes Nov 12, 2025
cb09d77
Revert "hs-dbg-view: Guard all dependencies behind flags"
alt-romes Nov 12, 2025
3aac64f
hs-dbg-view: Add support for containers for in-transitive-closure unit
alt-romes Nov 12, 2025
ab31319
hs-dbg-view: Add initial support for other mods for in-memory unit
alt-romes Nov 13, 2025
8903b1c
magic: by Matthew Pickering
alt-romes Nov 13, 2025
fb29845
Add test for #47 depends on containers but not on haskell-debugger-view
alt-romes Nov 13, 2025
a1d20e2
feat: Add remaining custom views for Text and ByteString
alt-romes Nov 13, 2025
8348ff7
ci: Package and use haskell-debugger-view
alt-romes Nov 13, 2025
d3d8471
Add haskell-debugger-view modules to extra-source-files
alt-romes Nov 14, 2025
bc30805
cleanup: Use structured logging instead of GHC's logger
alt-romes Nov 14, 2025
eab28e8
Use time Recorder as the GHC logger
alt-romes Nov 14, 2025
0cd0e58
revert: Use debuggerLogAction for GHC internal output.
alt-romes Nov 14, 2025
32ca05e
Omit in-memory compilation output
alt-romes Nov 14, 2025
644d9f1
test: Add simpler step-out test
alt-romes Nov 14, 2025
af6e0a1
refactor: Move one line down and comment other
alt-romes Nov 14, 2025
365645c
fix: Never strip interface pragmas & unfoldings
alt-romes Nov 17, 2025
ef2a51c
Revert "fix: Never strip interface pragmas & unfoldings"
alt-romes Nov 17, 2025
0d18ff4
fix: Pass extraGhcArgs to ALL units
alt-romes Nov 17, 2025
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
14 changes: 12 additions & 2 deletions .github/workflows/debugger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ on:
pull_request: {}
workflow_call: {}

env:
haskellDebuggerViewVersion: 0.1.0.0
haskellDebuggerVersion: 0.10.0.0

name: Debugger CI
jobs:
build-vscode-extension:
Expand Down Expand Up @@ -42,10 +46,14 @@ jobs:
- name: Package hdb
run: cabal sdist

- name: Package haskell-debugger-view
run: cabal sdist haskell-debugger-view

- name: Upload haskell-debugger source distribution
uses: actions/upload-artifact@v4
with:
name: Haskell Debugger Source Distribution
# This will catch both haskell-debugger and haskell-debugger-view
path: dist-newstyle/sdist/haskell-debugger*.tar.gz

# Building and testing are done on the distributed sdist: not on the full git repo.
Expand Down Expand Up @@ -95,8 +103,10 @@ jobs:
- name: Build and Run integration tests
run: |
echo "Running DAP Integration Testsuite now..."
(cd ${{ runner.temp }}/dist && tar xzf *.tar.gz && rm *.tar.gz && cd haskell-debugger-* && cabal build exe:hdb --enable-executable-dynamic --allow-newer=ghc-bignum,containers,time,ghc,base,template-haskell)
export DEBUGGER=$(cd ${{ runner.temp }}/dist/haskell-debugger-* && cabal list-bin exe:hdb --enable-executable-dynamic --allow-newer=ghc-bignum,containers,time,ghc,base,template-haskell)
(cd ${{ runner.temp }}/dist && for f in *.tar.gz; do tar xzf "$f"; done)
(cd ${{ runner.temp }}/dist && cd haskell-debugger-${{env.haskellDebuggerVersion}} && echo 'packages: . ../haskell-debugger-view-${{env.haskellDebuggerViewVersion}}' > cabal.project)
(cd ${{ runner.temp }}/dist && cd haskell-debugger-${{env.haskellDebuggerVersion}} && cabal build exe:hdb --enable-executable-dynamic --allow-newer=ghc-bignum,containers,time,ghc,base,template-haskell)
export DEBUGGER=$(cd ${{ runner.temp }}/dist/haskell-debugger-${{env.haskellDebuggerVersion}} && cabal list-bin exe:hdb --enable-executable-dynamic --allow-newer=ghc-bignum,containers,time,ghc,base,template-haskell)
echo "DEBUGGER: $DEBUGGER"
cd test/integration-tests/
make clean
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ jobs:
with:
pat: ${{ secrets.VS_MARKETPLACE_TOKEN }}
registryUrl: https://marketplace.visualstudio.com
extensionFile: ${{ runner.temp }}/extension/haskell-debugger-extension-0.9.0.vsix
extensionFile: ${{ runner.temp }}/extension/haskell-debugger-extension-0.10.0.vsix

# Publish a new hackage release of the haskell-debugger
- name: Hackage Release
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Revision history for haskell-debugger

## 0.10.0.0 -- Unreleased

## 0.9.0.0 -- 2025-10-13

### Main changes
Expand Down
2 changes: 1 addition & 1 deletion cabal.project
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
packages: .
packages: . haskell-debugger-view

allow-newer: ghc-bignum,containers,time,ghc,base,template-haskell

Expand Down
5 changes: 5 additions & 0 deletions haskell-debugger-view/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Revision history for haskell-debugger-view

## 0.1.0.0 -- YYYY-mm-dd

* First version. Released on an unsuspecting world.
27 changes: 27 additions & 0 deletions haskell-debugger-view/haskell-debugger-view.cabal
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
cabal-version: 3.12
name: haskell-debugger-view
version: 0.1.0.0
license: NONE
author: Matthew Pickering
maintainer: matthewtpickering@gmail.com
build-type: Simple
extra-doc-files: CHANGELOG.md

common warnings
ghc-options: -Wall

library
import: warnings
-- If you add a module here make sure to also add this module to the list
-- of modules that we attempt to load when haskell-debug-view is not
-- dependend upon transitively, in GHC.Debugger.Session.Builtin
exposed-modules: GHC.Debugger.View.Class
GHC.Debugger.View.Containers
GHC.Debugger.View.Text
GHC.Debugger.View.ByteString
build-depends: base,
containers >= 0.7 && < 0.9,
text >= 2.1 && < 2.3,
bytestring >= 0.12.1 && < 0.13,
hs-source-dirs: src
default-language: GHC2021
11 changes: 11 additions & 0 deletions haskell-debugger-view/src/GHC/Debugger/View/ByteString.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{-# OPTIONS_GHC -Wno-orphans #-}
module GHC.Debugger.View.ByteString where

import GHC.Debugger.View.Class

import qualified Data.ByteString as BS

instance DebugView BS.ByteString where
debugValue t = VarValue (show t) False
debugFields _ = VarFields []

133 changes: 133 additions & 0 deletions haskell-debugger-view/src/GHC/Debugger/View/Class.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
{-# LANGUAGE DerivingVia, StandaloneDeriving, ViewPatterns, ImpredicativeTypes #-}
module GHC.Debugger.View.Class where

-- | The representation of the value for some variable on the debugger
data VarValue = VarValue
{ -- | The value to display inline for this variable
varValue :: String

-- | Can this variable further be expanded (s.t. @'debugFields'@ is not null?)
, varExpandable :: Bool
}
deriving (Show, Read)

-- | The representation for fields of a value which is expandable in the debugger
newtype VarFields = VarFields
{ varFields :: [(String, VarFieldValue)]
}

-- | A box for subfields of a value.
--
-- Used to construct the debug-view list of fields one gets from expanding a datatype.
-- See, for instance, the @DebugView (a, b)@ instance for an example of how it is used.
--
-- The boxed value is returned as is and can be further forced or expanded by
-- the debugger, using either the existing @'DebugView'@ instance for the
-- existential @a@ (the instance is found at runtime), or the generic runtime
-- term inspection mechanisms otherwise.
data VarFieldValue = forall a. VarFieldValue a

-- | Custom handling of debug terms (e.g. in the variables pane, or when
-- inspecting a lazy variable)
class DebugView a where

-- | Compute the representation of a variable with the given value.
--
-- INVARIANT: this method should only called on values which are already in
-- WHNF, never thunks.
--
-- That said, this method is responsible for determining how much it is
-- forced when displaying it inline as a variable.
--
-- For instance, for @String@, @a@ will be fully forced to display the entire
-- string in one go rather than as a linked list of @'Char'@.
debugValue :: a -> VarValue

-- | Compute the fields to display when expanding a value of type @a@.
--
-- This method should only be called to get the fields if the corresponding
-- @'VarValue'@ has @'varExpandable' = True@.
debugFields :: a -> VarFields

--------------------------------------------------------------------------------

-- | Boring types scaffolding.
--
-- Meant to be used like:
--
-- @
-- deriving via (BoringTy Int) instance (DebugView Int)
-- @
--
-- to derive a 'DebugView' for a type whose terms should always be fully forced
-- and displayed whole rather than as parts.
--
-- A boring type is one for which we don't care about the structure and would
-- rather see "whole" when being inspected. Strings and literals are a good
-- example, because it's more useful to see the string value than it is to see
-- a linked list of characters where each has to be forced individually.
newtype BoringTy a = BoringTy a

instance Show a => DebugView (BoringTy a) where
debugValue (BoringTy x) = VarValue (show x) False
debugFields _ = VarFields []

deriving via BoringTy Int instance DebugView Int
deriving via BoringTy Word instance DebugView Word
deriving via BoringTy Double instance DebugView Double
deriving via BoringTy Float instance DebugView Float
deriving via BoringTy Integer instance DebugView Integer
deriving via BoringTy Char instance DebugView Char
deriving via BoringTy String instance DebugView String

instance DebugView (a, b) where
debugValue _ = VarValue "( , )" True
debugFields (x, y) = VarFields
[ ("fst", VarFieldValue x)
, ("snd", VarFieldValue y) ]

-- instance DebugView T.Text where
-- debugValue t = VarValue (show (T.unpack t)) False
-- debugFields _ = VarFields []
--
-- instance DebugView BS.ByteString where
-- debugValue t = VarValue (show (T.unpack (T.decodeUtf8 t))) False
-- debugFields _ = VarFields []
--
-- instance DebugView (IM.IntMap a) where
-- debugValue _ = VarValue "IntMap" True
-- debugFields im = VarFields
-- [ (show k, VarFieldValue v)
-- | (k, v) <- IM.toList im
-- ]
--
-- instance Show k => DebugView (M.Map k a) where
-- debugValue _ = VarValue "Map" True
-- debugFields m = VarFields
-- [ (show k, VarFieldValue v)
-- | (k, v) <- M.toList m
-- ]

--------------------------------------------------------------------------------
-- * (Internal) Wrappers required to call `evalStmt` on methods more easily
--------------------------------------------------------------------------------

-- | Wrapper to make evaluating from debugger easier
data VarValueIO = VarValueIO
{ varValueIO :: IO String
, varExpandableIO :: Bool
}

debugValueIOWrapper :: DebugView a => a -> IO [VarValueIO]
debugValueIOWrapper x = case debugValue x of
VarValue str b ->
pure [VarValueIO (pure str) b]

newtype VarFieldsIO = VarFieldsIO
{ varFieldsIO :: [(IO String, VarFieldValue)]
}

debugFieldsIOWrapper :: DebugView a => a -> IO [VarFieldsIO]
debugFieldsIOWrapper x = case debugFields x of
VarFields fls ->
pure [VarFieldsIO [ (pure fl_s, b) | (fl_s, b) <- fls]]
21 changes: 21 additions & 0 deletions haskell-debugger-view/src/GHC/Debugger/View/Containers.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{-# OPTIONS_GHC -Wno-orphans #-}
module GHC.Debugger.View.Containers where

import GHC.Debugger.View.Class

import qualified Data.IntMap as IM
import qualified Data.Map as M

instance DebugView (IM.IntMap a) where
debugValue _ = VarValue "IntMap" True
debugFields im = VarFields
[ (show k, VarFieldValue v)
| (k, v) <- IM.toList im
]

instance Show k => DebugView (M.Map k a) where
debugValue _ = VarValue "Map" True
debugFields m = VarFields
[ (show k, VarFieldValue v)
| (k, v) <- M.toList m
]
11 changes: 11 additions & 0 deletions haskell-debugger-view/src/GHC/Debugger/View/Text.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{-# OPTIONS_GHC -Wno-orphans #-}
module GHC.Debugger.View.Text where

import GHC.Debugger.View.Class

import qualified Data.Text as T

instance DebugView T.Text where
debugValue t = VarValue (show (T.unpack t)) False
debugFields _ = VarFields []

17 changes: 16 additions & 1 deletion haskell-debugger.cabal
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cabal-version: 3.12
name: haskell-debugger
version: 0.9.0.0
version: 0.10.0.0
synopsis:
A step-through debugger for GHC Haskell

Expand Down Expand Up @@ -31,6 +31,13 @@ category: Development
build-type: Simple
extra-doc-files: CHANGELOG.md
README.md

-- Make sure to list all files which we embed with TH, so sdist + build works.
extra-source-files: haskell-debugger-view/src/GHC/Debugger/View/Class.hs
haskell-debugger-view/src/GHC/Debugger/View/Containers.hs
haskell-debugger-view/src/GHC/Debugger/View/Text.hs
haskell-debugger-view/src/GHC/Debugger/View/ByteString.hs

homepage: https://github.com/well-typed/haskell-debugger
bug-reports: https://github.com/well-typed/haskell-debugger/issues

Expand All @@ -57,17 +64,22 @@ library
GHC.Debugger.Stopped.Variables,
GHC.Debugger.Utils,
GHC.Debugger.Runtime,
GHC.Debugger.Runtime.Instances,

GHC.Debugger.Runtime.Term.Key,
GHC.Debugger.Runtime.Term.Cache,

GHC.Debugger.Monad,

GHC.Debugger.Session,
GHC.Debugger.Session.Builtin,
GHC.Debugger.Interface.Messages
-- other-modules:
default-extensions: CPP
build-depends: base > 4.21 && < 5,
ghc >= 9.14 && < 9.16, ghci >= 9.14 && < 9.16,
ghc-boot-th >= 9.14 && < 9.16,
ghc-boot >= 9.14 && < 9.16,
array >= 0.5.8 && < 0.6,
containers >= 0.7 && < 0.9,
mtl >= 2.3 && < 3,
Expand All @@ -82,12 +94,15 @@ library
base16-bytestring >= 1.0.2.0 && < 1.1,
aeson >= 2.2.3 && < 2.3,
hie-bios >= 0.15 && < 0.18,
file-embed >= 0.0.16 && < 0.1,
-- Logger dependencies
time >= 1.14 && < 2,
prettyprinter >= 1.7.1 && < 2,
text >= 2.1 && < 2.3,
co-log-core >= 0.3.2.5 && < 0.4,

haskell-debugger-view >= 0.1 && < 1.0

hs-source-dirs: haskell-debugger
default-language: GHC2021

Expand Down
14 changes: 2 additions & 12 deletions haskell-debugger/GHC/Debugger.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import GHC.Debugger.Breakpoint
import GHC.Debugger.Evaluation
import GHC.Debugger.Stopped
import GHC.Debugger.Monad
import GHC.Debugger.Utils
import GHC.Debugger.Interface.Messages
import GHC.Debugger.Logger

Expand All @@ -26,17 +25,7 @@ execute recorder = \case
SetBreakpoint{brk, hitCount, condition} ->
DidSetBreakpoint <$> setBreakpoint brk (condBreakEnableStatus hitCount condition)
DelBreakpoint bp -> DidRemoveBreakpoint <$> setBreakpoint bp BreakpointDisabled
GetBreakpointsAt ModuleBreak{path, lineNum, columnNum} -> do
mmodl <- getModuleByPath path
case mmodl of
Left e -> do
displayWarnings [e]
return $ DidGetBreakpoints Nothing
Right modl -> do
mbfnd <- getBreakpointsAt modl lineNum columnNum
return $
DidGetBreakpoints (realSrcSpanToSourceSpan . snd <$> mbfnd)
GetBreakpointsAt _ -> error "unexpected getbreakpoints without ModuleBreak"
GetBreakpointsAt bp -> DidGetBreakpoints <$> getBreakpointsAt bp
GetStacktrace -> GotStacktrace <$> getStacktrace
GetScopes -> GotScopes <$> getScopes
GetVariables kind -> GotVariables <$> getVariables kind
Expand All @@ -56,3 +45,4 @@ data DebuggerLog
instance Pretty DebuggerLog where
pretty = \ case
EvalLog msg -> pretty msg

Loading
Loading