1- import type * as Array from "effect/Array"
1+ import * as Array from "effect/Array"
2+ import * as DateTime from "effect/DateTime"
3+ import * as Duration from "effect/Duration"
24import * as Effect from "effect/Effect"
35import * as Option from "effect/Option"
46import * as Stream from "effect/Stream"
57import * as SubscriptionRef from "effect/SubscriptionRef"
68import * as vscode from "vscode"
9+ import type * as DebugChannel from "./DebugChannel"
710import * as Debug from "./DebugEnv"
11+ import * as DurationUtils from "./utils/Duration"
812import { registerCommand , revealFile , TreeDataProvider , treeDataProvider } from "./VsCode"
913
10- class TreeNode {
14+ class FiberNode {
15+ readonly _tag = "FiberNode"
16+ public interruptionRequested = false
1117 constructor ( readonly entry : Debug . FiberEntry ) { }
1218}
1319
20+ class FiberMetadataNode {
21+ readonly _tag = "FiberMetadataNode"
22+ constructor ( readonly name : string , readonly value : string ) { }
23+ }
24+
25+ class AttributeNode {
26+ readonly _tag = "AttributeNode"
27+ constructor ( readonly name : string , readonly variable : DebugChannel . VariableReference ) { }
28+ }
29+
30+ class VariableNode {
31+ readonly _tag = "VariableNode"
32+ constructor ( readonly variable : DebugChannel . VariableReference ) { }
33+ }
34+
35+ type TreeNode = FiberNode | FiberMetadataNode | AttributeNode | VariableNode
36+
1437export const DebugFibersProviderLive = treeDataProvider < TreeNode > ( "effect-debug-fibers" ) (
1538 ( refresh ) =>
1639 Effect . gen ( function * ( ) {
1740 const debug = yield * Debug . DebugEnv
1841 let nodes : Array < TreeNode > = [ ]
1942
2043 yield * registerCommand ( "effect.revealFiberCurrentSpan" , ( node : TreeNode ) => {
21- if ( node && node . entry . stack . length > 0 ) {
44+ if ( node && node . _tag === "FiberNode" && node . entry . stack . length > 0 ) {
2245 const stackEntry = node . entry . stack [ 0 ]
2346 if ( stackEntry . path ) {
2447 return revealFile (
@@ -30,15 +53,25 @@ export const DebugFibersProviderLive = treeDataProvider<TreeNode>("effect-debug-
3053 return Effect . void
3154 } )
3255
56+ yield * registerCommand ( "effect.interruptDebugFiber" , ( node : TreeNode ) => {
57+ if ( node && node . _tag === "FiberNode" ) {
58+ return node . entry . interrupt . pipe (
59+ Effect . ensuring ( Effect . sync ( ( ) => node . interruptionRequested = true ) ) ,
60+ Effect . ensuring ( refresh ( Option . some ( node ) ) )
61+ )
62+ }
63+ return Effect . void
64+ } )
65+
3366 const capture = ( threadId ?: number ) =>
3467 Effect . gen ( function * ( ) {
3568 const sessionOption = yield * ( SubscriptionRef . get ( debug . session ) )
3669 if ( Option . isNone ( sessionOption ) ) {
3770 nodes = [ ]
3871 } else {
3972 const session = sessionOption . value
40- const pairs = yield * ( session . currentFibers ( threadId ) )
41- nodes = pairs . map ( ( _ ) => new TreeNode ( _ ) )
73+ const rootData = yield * ( session . currentFibers ( threadId ) )
74+ nodes = rootData . map ( ( _ ) => new FiberNode ( _ ) )
4275 }
4376 yield * ( refresh ( Option . none ( ) ) )
4477 } )
@@ -67,7 +100,55 @@ export const DebugFibersProviderLive = treeDataProvider<TreeNode>("effect-debug-
67100 return TreeDataProvider < TreeNode > ( {
68101 children : Option . match ( {
69102 onNone : ( ) => Effect . succeedSome ( nodes ) ,
70- onSome : ( ) => Effect . succeedNone
103+ onSome : ( node ) => {
104+ switch ( node . _tag ) {
105+ case "FiberNode" : {
106+ const childs : Array < TreeNode > = [
107+ new FiberMetadataNode (
108+ "Started At" ,
109+ DateTime . make ( node . entry . startTimeMillis ) . pipe (
110+ Option . map (
111+ DateTime . formatLocal ( { dateStyle : "medium" , timeStyle : "long" } )
112+ ) ,
113+ Option . getOrElse ( ( ) => String ( node . entry . startTimeMillis ) )
114+ )
115+ ) ,
116+ new FiberMetadataNode (
117+ "Lifetime" ,
118+ DurationUtils . format ( Duration . millis ( node . entry . lifeTimeMillis ) )
119+ ) ,
120+ new FiberMetadataNode (
121+ "Interruptible" ,
122+ node . entry . isInterruptible ? "true" : "false"
123+ ) ,
124+ new FiberMetadataNode (
125+ "Interrupted" ,
126+ node . entry . isInterrupted ? "true" : "false"
127+ ) ,
128+ ...node . entry . stack [ 0 ] ?. attributes . map ( ( [ name , variable ] ) => new AttributeNode ( name , variable ) ) ?? [ ] ,
129+ ...nodes . filter ( ( _ ) => _ . _tag === "FiberNode" && node . entry . children . includes ( _ . entry . id ) )
130+ ]
131+ return Effect . succeedSome ( childs )
132+ }
133+ case "FiberMetadataNode" : {
134+ return Effect . succeedNone
135+ }
136+ case "AttributeNode" : {
137+ return node . variable . children . pipe (
138+ Effect . map ( Array . map ( ( _ ) => new VariableNode ( _ ) ) ) ,
139+ Effect . orElseSucceed ( ( ) => [ ] ) ,
140+ Effect . asSome
141+ )
142+ }
143+ case "VariableNode" : {
144+ return node . variable . children . pipe (
145+ Effect . map ( Array . map ( ( _ ) => new VariableNode ( _ ) ) ) ,
146+ Effect . orElseSucceed ( ( ) => [ ] ) ,
147+ Effect . asSome
148+ )
149+ }
150+ }
151+ }
71152 } ) ,
72153 treeItem : ( node ) => Effect . succeed ( treeItem ( node ) )
73154 } )
@@ -77,20 +158,57 @@ export const DebugFibersProviderLive = treeDataProvider<TreeNode>("effect-debug-
77158// === helpers ===
78159
79160const treeItem = ( node : TreeNode ) : vscode . TreeItem => {
80- const item = new vscode . TreeItem (
81- "Fiber#" + node . entry . id + ( node . entry . isInterruptible ? "" : " (uninterruptible)" ) ,
82- vscode . TreeItemCollapsibleState . None
83- )
84- if ( node . entry . isCurrent ) {
85- item . iconPath = new vscode . ThemeIcon ( "arrow-small-right" )
86- }
87- const firstEntry = node . entry . stack [ 0 ]
88- if ( firstEntry ) {
89- item . description = firstEntry . name
90- if ( firstEntry . path ) {
91- item . tooltip = firstEntry . path + ":" + firstEntry . line + ":" + firstEntry . column
161+ switch ( node . _tag ) {
162+ case "FiberNode" : {
163+ const item = new vscode . TreeItem (
164+ "Fiber#" + node . entry . id + ( node . entry . isInterrupted ? " (interrupting)" : "" ) +
165+ ( node . entry . isInterruptible ? "" : " (uninterruptible)" ) +
166+ ( node . interruptionRequested ? " (interruption requested)" : "" ) ,
167+ vscode . TreeItemCollapsibleState . Collapsed
168+ )
169+ item . contextValue = node . interruptionRequested || node . entry . isInterrupted ? "fiber-interrupting" : "fiber"
170+ if ( node . entry . isCurrent ) {
171+ item . iconPath = new vscode . ThemeIcon ( "arrow-small-right" )
172+ }
173+ const firstEntry = node . entry . stack [ 0 ]
174+ if ( firstEntry ) {
175+ item . description = firstEntry . name
176+ if ( firstEntry . path ) {
177+ item . tooltip = firstEntry . path + ":" + firstEntry . line + ":" + firstEntry . column
178+ }
179+ }
180+
181+ return item
182+ }
183+ case "FiberMetadataNode" : {
184+ const item = new vscode . TreeItem ( node . name , vscode . TreeItemCollapsibleState . None )
185+ item . contextValue = "fiberMetadata"
186+ item . description = node . value
187+ return item
188+ }
189+ case "AttributeNode" : {
190+ const item = new vscode . TreeItem (
191+ node . name + ":" ,
192+ node . variable . isContainer
193+ ? vscode . TreeItemCollapsibleState . Collapsed
194+ : vscode . TreeItemCollapsibleState . None
195+ )
196+ item . contextValue = "attribute"
197+ item . description = node . variable . value
198+ item . tooltip = node . variable . value
199+ return item
200+ }
201+ case "VariableNode" : {
202+ const item = new vscode . TreeItem (
203+ node . variable . name + ":" ,
204+ node . variable . isContainer
205+ ? vscode . TreeItemCollapsibleState . Collapsed
206+ : vscode . TreeItemCollapsibleState . None
207+ )
208+ item . contextValue = "variable"
209+ item . description = node . variable . value
210+ item . tooltip = node . variable . value
211+ return item
92212 }
93213 }
94-
95- return item
96214}
0 commit comments