From 4ab7a9a282b0f6fd1678b36ff0a5def5c6566e49 Mon Sep 17 00:00:00 2001 From: ivojawer Date: Tue, 19 Nov 2024 18:16:18 -0300 Subject: [PATCH 01/13] printing execution result --- packages/debug-adapter/src/debug-session.ts | 9 ++++- .../src/test/debug-adapter.test.ts | 35 +++++++++++++++++++ .../src/test/fixtures/aTest.wtest | 10 ++++++ 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 packages/debug-adapter/src/test/fixtures/aTest.wtest diff --git a/packages/debug-adapter/src/debug-session.ts b/packages/debug-adapter/src/debug-session.ts index 7c2f77c..7847e05 100644 --- a/packages/debug-adapter/src/debug-session.ts +++ b/packages/debug-adapter/src/debug-session.ts @@ -1,4 +1,4 @@ -import { DebugSession, InitializedEvent, Source, StackFrame, StoppedEvent, TerminatedEvent, Thread, Variable } from '@vscode/debugadapter' +import { DebugSession, InitializedEvent, OutputEvent, Source, StackFrame, StoppedEvent, TerminatedEvent, Thread, Variable } from '@vscode/debugadapter' import { DebugProtocol } from '@vscode/debugprotocol' import path = require('path') import * as vscode from 'vscode' @@ -166,6 +166,13 @@ export class WollokDebugSession extends DebugSession { this.stoppedNode = state.next this.sendEvent(new StoppedEvent(stoppedReason, WollokDebugSession.THREAD_ID)) } else { + if(state.error) { + this.sendEvent(new OutputEvent(state.error.message, 'stderr')) + } else { + if(state.done) { + this.sendEvent(new OutputEvent('Finished executing without errors', 'stdout')) + } + } this.sendEvent(new TerminatedEvent()) } } diff --git a/packages/debug-adapter/src/test/debug-adapter.test.ts b/packages/debug-adapter/src/test/debug-adapter.test.ts index 38aa847..71b06c9 100644 --- a/packages/debug-adapter/src/test/debug-adapter.test.ts +++ b/packages/debug-adapter/src/test/debug-adapter.test.ts @@ -6,6 +6,7 @@ import { DebugProtocol } from '@vscode/debugprotocol' const DEBUG_ADAPTER = path.resolve(__dirname, 'start-debug-session.js') const FIXTURES_ROOT = path.resolve(__dirname, '../../../../packages/debug-adapter/src/test/fixtures') const PROGRAM = path.resolve(FIXTURES_ROOT, 'aProgram.wpgm') +const TEST_FILE = path.resolve(FIXTURES_ROOT, 'aTest.wtest') const WLK = path.resolve(FIXTURES_ROOT, 'anObject.wlk') describe('debug adapter', function () { @@ -132,6 +133,40 @@ describe('debug adapter', function () { })) }) }) + + describe('finished execution', function (){ + it('finishing without errors', async function (){ + await Promise.all([ + dc.launch({ + "stopOnEntry": false, + "file": TEST_FILE, + "target": { + "describe": "some tests", + "test": "does not break", + }, + }), + dc.configurationDoneRequest(), + ]) + await dc.assertOutput('stdout', "Finished executing without errors", 1000) + await dc.waitForEvent('terminated', 1000) + }) + + it('finishing with errors', async function (){ + await Promise.all([ + dc.launch({ + "stopOnEntry": false, + "file": TEST_FILE, + "target": { + "describe": "some tests", + "test": "breaks", + }, + }), + dc.configurationDoneRequest(), + ]) + await dc.assertOutput('stderr', "My exception message", 1000) + await dc.waitForEvent('terminated', 1000) + }) + }) }) diff --git a/packages/debug-adapter/src/test/fixtures/aTest.wtest b/packages/debug-adapter/src/test/fixtures/aTest.wtest new file mode 100644 index 0000000..0677e8e --- /dev/null +++ b/packages/debug-adapter/src/test/fixtures/aTest.wtest @@ -0,0 +1,10 @@ +describe "some tests" { + test "breaks" { + throw new DomainException(message = "My exception message") + assert.that(true) + } + + test "does not break" { + assert.that(true) + } +} \ No newline at end of file From 994135d66483e5a17a387b1f8ab8e728f51b849f Mon Sep 17 00:00:00 2001 From: ivojawer Date: Wed, 20 Nov 2024 00:58:15 -0300 Subject: [PATCH 02/13] skip synthetic nodes --- packages/debug-adapter/src/debug-session.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/debug-adapter/src/debug-session.ts b/packages/debug-adapter/src/debug-session.ts index 7847e05..98c9d6b 100644 --- a/packages/debug-adapter/src/debug-session.ts +++ b/packages/debug-adapter/src/debug-session.ts @@ -164,14 +164,19 @@ export class WollokDebugSession extends DebugSession { const stoppedReason = overrideStoppedReason || (state.done ? state.error ? 'exception' : 'done' : 'breakpoint') if(!state.done && 'next' in state) { this.stoppedNode = state.next + if(this.stoppedNode.isSynthetic) { + return this.moveExecution(() => this.executionDirector.stepOver(), overrideStoppedReason) + } this.sendEvent(new StoppedEvent(stoppedReason, WollokDebugSession.THREAD_ID)) } else { if(state.error) { - this.sendEvent(new OutputEvent(state.error.message, 'stderr')) + this.sendEvent(new OutputEvent(state.error.message, 'stderr', { + source: this.sourceFromNode(this.stoppedNode), + line: this.stoppedNode.sourceMap?.start.line, + column: this.stoppedNode.sourceMap?.start.column, + })) } else { - if(state.done) { this.sendEvent(new OutputEvent('Finished executing without errors', 'stdout')) - } } this.sendEvent(new TerminatedEvent()) } From cb3d4016f980ac26b1e3bb0c59fd35fbb71adac5 Mon Sep 17 00:00:00 2001 From: ivojawer Date: Wed, 20 Nov 2024 01:30:16 -0300 Subject: [PATCH 03/13] position converter --- packages/debug-adapter/src/debug-session.ts | 19 +++++------ .../src/utils/wollok-position-converter.ts | 34 +++++++++++++++++++ 2 files changed, 42 insertions(+), 11 deletions(-) create mode 100644 packages/debug-adapter/src/utils/wollok-position-converter.ts diff --git a/packages/debug-adapter/src/debug-session.ts b/packages/debug-adapter/src/debug-session.ts index 98c9d6b..6ccd3f5 100644 --- a/packages/debug-adapter/src/debug-session.ts +++ b/packages/debug-adapter/src/debug-session.ts @@ -3,6 +3,7 @@ import { DebugProtocol } from '@vscode/debugprotocol' import path = require('path') import * as vscode from 'vscode' import { Body, BOOLEAN_MODULE, buildEnvironment, Context, DirectedInterpreter, Environment, ExecutionDirector, executionFor, ExecutionState, FileContent, Frame, is, LIST_MODULE, Node, NUMBER_MODULE, Package, Program, PROGRAM_FILE_EXTENSION, RuntimeObject, RuntimeValue, Sentence, STRING_MODULE, Test, TEST_FILE_EXTENSION, WOLLOK_FILE_EXTENSION } from 'wollok-ts' +import { WollokPositionConverter } from './utils/wollok-position-converter' export class WollokDebugSession extends DebugSession { protected static readonly THREAD_ID = 1 protected static WOLLOK_PATH_SEPARATOR = '/' @@ -17,6 +18,7 @@ export class WollokDebugSession extends DebugSession { protected configurationDone: Promise protected notifyConfigurationDone: () => void + protected positionConverter: WollokPositionConverter constructor(protected workspace: typeof vscode.workspace){ super() @@ -30,7 +32,7 @@ export class WollokDebugSession extends DebugSession { this.notifyConfigurationDone() } - protected initializeRequest(response: DebugProtocol.InitializeResponse, _args: DebugProtocol.InitializeRequestArguments): void { + protected initializeRequest(response: DebugProtocol.InitializeResponse, args: DebugProtocol.InitializeRequestArguments): void { // capabilities response.body = { ...response.body, @@ -40,6 +42,8 @@ export class WollokDebugSession extends DebugSession { supportsSingleThreadExecutionRequests: false, } + this.positionConverter = new WollokPositionConverter(args.linesStartAt1, args.columnsStartAt1) + // initialize wollok interpreter const debuggableFileExtensions = [WOLLOK_FILE_EXTENSION, PROGRAM_FILE_EXTENSION, TEST_FILE_EXTENSION] this.workspace.findFiles(`**/*.{${debuggableFileExtensions.join(',')}}`).then(async files => { @@ -137,10 +141,7 @@ export class WollokDebugSession extends DebugSession { response.body = { breakpoints: sentences.map(sentence => ({ verified: true, - line: sentence.sourceMap.start.line, - column: sentence.sourceMap.start.column, - endColumn: sentence.sourceMap.end.column, - endLine: sentence.sourceMap.end.line, + ...this.positionConverter.convertSourceMapToClient(sentence.sourceMap), source: this.sourceFromNode(sentence), }) ), @@ -172,8 +173,7 @@ export class WollokDebugSession extends DebugSession { if(state.error) { this.sendEvent(new OutputEvent(state.error.message, 'stderr', { source: this.sourceFromNode(this.stoppedNode), - line: this.stoppedNode.sourceMap?.start.line, - column: this.stoppedNode.sourceMap?.start.column, + ...this.stoppedNode.sourceMap && this.positionConverter.convertPositionToClient(this.stoppedNode.sourceMap?.start), })) } else { this.sendEvent(new OutputEvent('Finished executing without errors', 'stdout')) @@ -224,10 +224,7 @@ export class WollokDebugSession extends DebugSession { return { id: this.frames.getIdFor(frame), name: frame.description, - line: currentNode.sourceMap?.start.line, - column: currentNode.sourceMap?.start.column, - endColumn: currentNode.sourceMap?.end.column, - endLine: currentNode.sourceMap?.end.line, + ...currentNode.sourceMap && this.positionConverter.convertSourceMapToClient(currentNode.sourceMap), source: !!currentNode.sourceFileName && this.sourceFromNode(currentNode), } } diff --git a/packages/debug-adapter/src/utils/wollok-position-converter.ts b/packages/debug-adapter/src/utils/wollok-position-converter.ts new file mode 100644 index 0000000..73e4d7c --- /dev/null +++ b/packages/debug-adapter/src/utils/wollok-position-converter.ts @@ -0,0 +1,34 @@ +import { SourceIndex, SourceMap } from 'wollok-ts' + +export class WollokPositionConverter { + constructor( + private readonly lineStartsAt1: boolean, + private readonly columnStartsAt1: boolean + ) {} + + convertSourceMapToClient(sourceMap: SourceMap): {line: number, column: number, lineEnd: number, columnEnd: number} { + const { line, column } = this.convertPositionToClient(sourceMap.start) + const { line: lineEnd, column: columnEnd } = this.convertPositionToClient(sourceMap.end) + return { + line, + column, + lineEnd, + columnEnd, + } + } + + convertPositionToClient(position: SourceIndex): { line: number, column: number } { + return { + line: this.convertDebuggerLineToClient(position.line), + column: this.convertDebuggerColumnToClient(position.column), + } + } + + convertDebuggerLineToClient(line: number): number { + return this.lineStartsAt1 ? line : line - 1 + } + + convertDebuggerColumnToClient(column: number): number { + return this.columnStartsAt1 ? column : column - 1 + } +} \ No newline at end of file From 98268a0a5536d36acf1ae1856a4e45cca95c0d08 Mon Sep 17 00:00:00 2001 From: ivojawer Date: Wed, 20 Nov 2024 01:41:50 -0300 Subject: [PATCH 04/13] rm sourcemap on error since stoppedNode should be undefined --- packages/debug-adapter/src/debug-session.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/debug-adapter/src/debug-session.ts b/packages/debug-adapter/src/debug-session.ts index 6ccd3f5..0a4d5f0 100644 --- a/packages/debug-adapter/src/debug-session.ts +++ b/packages/debug-adapter/src/debug-session.ts @@ -171,9 +171,9 @@ export class WollokDebugSession extends DebugSession { this.sendEvent(new StoppedEvent(stoppedReason, WollokDebugSession.THREAD_ID)) } else { if(state.error) { - this.sendEvent(new OutputEvent(state.error.message, 'stderr', { + this.sendEvent(new OutputEvent(state.error.message, 'stderr', this.stoppedNode && { source: this.sourceFromNode(this.stoppedNode), - ...this.stoppedNode.sourceMap && this.positionConverter.convertPositionToClient(this.stoppedNode.sourceMap?.start), + ...this.positionConverter.convertPositionToClient(this.stoppedNode.sourceMap?.start), })) } else { this.sendEvent(new OutputEvent('Finished executing without errors', 'stdout')) From 60edfeea3402a5cf0d32999a6462bf9a73c62210 Mon Sep 17 00:00:00 2001 From: ivojawer Date: Fri, 22 Nov 2024 23:57:30 -0300 Subject: [PATCH 05/13] breakpoint locations request --- packages/debug-adapter/src/debug-session.ts | 66 ++++++++++++------- .../src/test/debug-adapter.test.ts | 33 ++++++++++ .../src/utils/wollok-position-converter.ts | 2 +- 3 files changed, 78 insertions(+), 23 deletions(-) diff --git a/packages/debug-adapter/src/debug-session.ts b/packages/debug-adapter/src/debug-session.ts index 0a4d5f0..8128e04 100644 --- a/packages/debug-adapter/src/debug-session.ts +++ b/packages/debug-adapter/src/debug-session.ts @@ -36,7 +36,7 @@ export class WollokDebugSession extends DebugSession { // capabilities response.body = { ...response.body, - // ToDo: supportsBreakpointLocationsRequest: true + supportsBreakpointLocationsRequest: true, supportsDelayedStackTraceLoading: true, supportsConfigurationDoneRequest: true, supportsSingleThreadExecutionRequests: false, @@ -119,30 +119,33 @@ export class WollokDebugSession extends DebugSession { } protected setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments, _request?: DebugProtocol.Request): void { - const sourcePath = this.toWollokPath(args.source.path) - const breakpointsPackage = this.environment.descendants.find(function (node): node is Package { - return node.is(Package) && node.sourceFileName === sourcePath - }) + const breakpointsPackage = this.packageFromSource(args.source as Source) + const breakpointsToRemove = [] this.executionDirector.breakpoints.forEach(breakpointedNode => { - if(breakpointedNode.sourceFileName === breakpointsPackage.sourceFileName) { - this.executionDirector.removeBreakpoint(breakpointedNode) + if(breakpointedNode.parentPackage.id === breakpointsPackage.id) { + breakpointsToRemove.push(breakpointedNode) } }) - - if(breakpointsPackage){ - const sentences = breakpointsPackage.descendants.filter(function (node): node is Sentence { - return node.is(Sentence) && node.parent.is(Body) && args.breakpoints.map(breakpoint => breakpoint.line).includes(node.sourceMap?.start.line) + breakpointsToRemove.forEach(breapointedNode => this.executionDirector.removeBreakpoint(breapointedNode)) + + if(breakpointsPackage) { + const nodesToBreakAt = breakpointsPackage.descendants.filter((node: Node) => { + return args.breakpoints.some(breakpoint => + breakpoint.column ? + this.positionConverter.convertDebuggerLineToClient(node.sourceMap?.start.line) === breakpoint.line && this.positionConverter.convertDebuggerColumnToClient(node.sourceMap?.start.column) === breakpoint.column : + node.is(Sentence) && node.parent.is(Body) && this.positionConverter.convertDebuggerLineToClient(node.sourceMap?.start.line) === breakpoint.line + ) }) - sentences.forEach(sentence => { + nodesToBreakAt.forEach(sentence => { this.executionDirector.addBreakpoint(sentence) }) response.body = { - breakpoints: sentences.map(sentence => ({ + breakpoints: nodesToBreakAt.map(node => ({ verified: true, - ...this.positionConverter.convertSourceMapToClient(sentence.sourceMap), - source: this.sourceFromNode(sentence), + ...this.positionConverter.convertSourceMapToClient(node.sourceMap), + source: this.sourceFromNode(node), }) ), @@ -151,9 +154,6 @@ export class WollokDebugSession extends DebugSession { this.sendResponse(response) } - protected setExceptionBreakPointsRequest(response: DebugProtocol.SetExceptionBreakpointsResponse, _args: DebugProtocol.SetExceptionBreakpointsArguments, _request?: DebugProtocol.Request): void { - this.sendResponse(response) - } protected moveExecution(action: () => ExecutionState, overrideStoppedReason?: string): void{ const state = action() @@ -171,10 +171,7 @@ export class WollokDebugSession extends DebugSession { this.sendEvent(new StoppedEvent(stoppedReason, WollokDebugSession.THREAD_ID)) } else { if(state.error) { - this.sendEvent(new OutputEvent(state.error.message, 'stderr', this.stoppedNode && { - source: this.sourceFromNode(this.stoppedNode), - ...this.positionConverter.convertPositionToClient(this.stoppedNode.sourceMap?.start), - })) + this.sendEvent(new OutputEvent(state.error.message, 'stderr')) } else { this.sendEvent(new OutputEvent('Finished executing without errors', 'stdout')) } @@ -229,6 +226,23 @@ export class WollokDebugSession extends DebugSession { } } + protected breakpointLocationsRequest(response: DebugProtocol.BreakpointLocationsResponse, args: DebugProtocol.BreakpointLocationsArguments, _request?: DebugProtocol.Request): void { + const pkg = this.packageFromSource(args.source as Source) + const breakpoints = pkg.descendants.filter(node => { + if(!node.sourceMap) return false + const nodeLocation = this.positionConverter.convertSourceMapToClient(node.sourceMap!) + return args.endLine ? + nodeLocation.line >= args.line && nodeLocation.lineEnd <= args.endLine && nodeLocation.column >= args.column && nodeLocation.columnEnd <= args.endColumn : + nodeLocation.line === args.line + }).map(node => this.positionConverter.convertSourceMapToClient(node.sourceMap!)) + + response.body = { + breakpoints, + } + + this.sendResponse(response) + } + protected scopesRequest(response: DebugProtocol.ScopesResponse, args: DebugProtocol.ScopesArguments, _request?: DebugProtocol.Request): void { const frame = this.frames.get(args.frameId) response.body = { @@ -296,6 +310,14 @@ export class WollokDebugSession extends DebugSession { private sourceFromNode(node: T): Source { return new Source(node.sourceFileName.split('/').pop()!, this.toClientPath(node.sourceFileName)) } + + private packageFromSource(source: Source): Package { + const pkg = this.environment.descendants.find(node => node.is(Package) && this.toWollokPath(source.path) === node.sourceFileName) as Package | undefined + if(!pkg) { + throw new Error(`Could not find package for source ${source.path}`) + } + return pkg + } } diff --git a/packages/debug-adapter/src/test/debug-adapter.test.ts b/packages/debug-adapter/src/test/debug-adapter.test.ts index 71b06c9..3d0b628 100644 --- a/packages/debug-adapter/src/test/debug-adapter.test.ts +++ b/packages/debug-adapter/src/test/debug-adapter.test.ts @@ -75,6 +75,39 @@ describe('debug adapter', function () { { column: 3, path: PROGRAM, line: 4, verified: true }, ) }) + + it('in-line breakpoint locations', async () => { + await Promise.all([ + dc.launch({ + "stopOnEntry": true, + "file": PROGRAM, + "target": { + "program": "aWollokProgram", + }, + }), + dc.configurationDoneRequest(), + ]) + + const response = await dc.send('breakpointLocations', { + source: { path: PROGRAM }, + line: 4, + }) + + const expectedBreakpoints = [ + { line: 4, column: 3, lineEnd: 4, columnEnd: 21 }, + { line: 4, column: 15, lineEnd: 5, columnEnd: 1 }, + ] + + assert.equal(response.body.breakpoints.length, expectedBreakpoints.length) + for(const location of response.body.breakpoints) { + assert(expectedBreakpoints.some(expected => + expected.line === location.line && + expected.column === location.column && + expected.lineEnd === location.lineEnd && + expected.columnEnd === location.columnEnd + )) + } + }) }) describe('stopped at breakpoint', function () { diff --git a/packages/debug-adapter/src/utils/wollok-position-converter.ts b/packages/debug-adapter/src/utils/wollok-position-converter.ts index 73e4d7c..056875b 100644 --- a/packages/debug-adapter/src/utils/wollok-position-converter.ts +++ b/packages/debug-adapter/src/utils/wollok-position-converter.ts @@ -29,6 +29,6 @@ export class WollokPositionConverter { } convertDebuggerColumnToClient(column: number): number { - return this.columnStartsAt1 ? column : column - 1 + return this.columnStartsAt1 ? column > 0 ? column : 1 : column - 1 } } \ No newline at end of file From 8c5287aedb721aac9738fa8d84f5b74d7906388d Mon Sep 17 00:00:00 2001 From: ivojawer Date: Sat, 30 Nov 2024 15:29:49 -0300 Subject: [PATCH 06/13] evaluate expressions mid-debug --- packages/debug-adapter/package.json | 3 +- packages/debug-adapter/src/debug-session.ts | 34 +++++++++++++++------ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/packages/debug-adapter/package.json b/packages/debug-adapter/package.json index 67cab7f..299e5e3 100644 --- a/packages/debug-adapter/package.json +++ b/packages/debug-adapter/package.json @@ -23,7 +23,6 @@ "@vscode/debugadapter-testsupport": "^1.67.0", "mocha": "^10.7.3", "nyc": "^17.0.0", - "ts-mocha": "^10.0.0", - "wollok-ts": "4.1.6" + "ts-mocha": "^10.0.0" } } diff --git a/packages/debug-adapter/src/debug-session.ts b/packages/debug-adapter/src/debug-session.ts index 8128e04..4457004 100644 --- a/packages/debug-adapter/src/debug-session.ts +++ b/packages/debug-adapter/src/debug-session.ts @@ -2,14 +2,13 @@ import { DebugSession, InitializedEvent, OutputEvent, Source, StackFrame, Stoppe import { DebugProtocol } from '@vscode/debugprotocol' import path = require('path') import * as vscode from 'vscode' -import { Body, BOOLEAN_MODULE, buildEnvironment, Context, DirectedInterpreter, Environment, ExecutionDirector, executionFor, ExecutionState, FileContent, Frame, is, LIST_MODULE, Node, NUMBER_MODULE, Package, Program, PROGRAM_FILE_EXTENSION, RuntimeObject, RuntimeValue, Sentence, STRING_MODULE, Test, TEST_FILE_EXTENSION, WOLLOK_FILE_EXTENSION } from 'wollok-ts' +import { Body, BOOLEAN_MODULE, buildEnvironment, Context, DirectedInterpreter, ExecutionDirector, executionFor, ExecutionState, FileContent, Frame, interprete, is, LIST_MODULE, Node, NUMBER_MODULE, Package, Program, PROGRAM_FILE_EXTENSION, RuntimeObject, RuntimeValue, Sentence, STRING_MODULE, Test, TEST_FILE_EXTENSION, WOLLOK_FILE_EXTENSION, Interpreter } from 'wollok-ts' import { WollokPositionConverter } from './utils/wollok-position-converter' export class WollokDebugSession extends DebugSession { protected static readonly THREAD_ID = 1 protected static WOLLOK_PATH_SEPARATOR = '/' protected interpreter: DirectedInterpreter - protected environment: Environment protected executionDirector: ExecutionDirector protected frames: WollokIdMap = new WollokIdMap() protected contexts: WollokIdMap = new WollokIdMap() @@ -54,15 +53,15 @@ export class WollokDebugSession extends DebugSession { })) )) - this.environment = buildEnvironment(wollokPackages, undefined) - this.interpreter = executionFor(this.environment) + const environment = buildEnvironment(wollokPackages, undefined) + this.interpreter = executionFor(environment) this.sendResponse(response) this.sendEvent(new InitializedEvent()) }) } protected launchRequest(response: DebugProtocol.LaunchResponse, args: WollokLaunchArguments, _request?: DebugProtocol.Request): void { - const containerPackage = this.environment.descendants.filter(is(Package)).find(pkg => pkg.sourceFileName === this.toWollokPath(args.file)) + const containerPackage = this.interpreter.evaluation.environment.descendants.filter(is(Package)).find(pkg => pkg.sourceFileName === this.toWollokPath(args.file)) if(!containerPackage){ this.sendErrorResponse(response, 404, 'Could not find target file') @@ -137,8 +136,8 @@ export class WollokDebugSession extends DebugSession { node.is(Sentence) && node.parent.is(Body) && this.positionConverter.convertDebuggerLineToClient(node.sourceMap?.start.line) === breakpoint.line ) }) - nodesToBreakAt.forEach(sentence => { - this.executionDirector.addBreakpoint(sentence) + nodesToBreakAt.forEach(node => { + this.executionDirector.addBreakpoint(node) }) response.body = { @@ -158,7 +157,7 @@ export class WollokDebugSession extends DebugSession { protected moveExecution(action: () => ExecutionState, overrideStoppedReason?: string): void{ const state = action() - // reset stack state when moving execution + // Reset stack state when moving execution this.frames.clear() this.contexts.clear() @@ -256,6 +255,23 @@ export class WollokDebugSession extends DebugSession { this.sendResponse(response) } + protected evaluateRequest(response: DebugProtocol.EvaluateResponse, args: DebugProtocol.EvaluateArguments, _request?: DebugProtocol.Request): void { + const currentEvaluation = args.context === 'repl' ? this.interpreter.evaluation : this.interpreter.evaluation.copy() + const frame = args.frameId ? this.frames.get(args.frameId) : currentEvaluation.currentFrame + const execution = interprete( + new Interpreter(currentEvaluation), + args.expression, + frame + ) + + response.body = { + result: execution.result, + variablesReference: 0, + } + + this.sendResponse(response) + } + protected toWollokPath(aPath: string): string { return aPath.replace(new RegExp( '\\' + path.sep, 'g'), WollokDebugSession.WOLLOK_PATH_SEPARATOR) } @@ -312,7 +328,7 @@ export class WollokDebugSession extends DebugSession { } private packageFromSource(source: Source): Package { - const pkg = this.environment.descendants.find(node => node.is(Package) && this.toWollokPath(source.path) === node.sourceFileName) as Package | undefined + const pkg = this.interpreter.evaluation.environment.descendants.find(node => node.is(Package) && this.toWollokPath(source.path) === node.sourceFileName) as Package | undefined if(!pkg) { throw new Error(`Could not find package for source ${source.path}`) } From dd5008ee9c12e95bb8b173d5de64bead2f044038 Mon Sep 17 00:00:00 2001 From: ivojawer Date: Sat, 30 Nov 2024 22:37:26 -0300 Subject: [PATCH 07/13] debugging from code lenses --- package.json | 33 +++++--- packages/client/src/commands.ts | 20 ++++- packages/client/src/shared-definitions.ts | 4 +- .../src/debug-configuration-provider.ts | 18 +---- packages/debug-adapter/src/debug-session.ts | 55 +++----------- packages/debug-adapter/src/target-finders.ts | 76 +++++++++++++++++++ .../src/utils/path-converters.ts | 10 +++ .../server/src/functionalities/code-lens.ts | 46 ++++++----- packages/server/src/shared-definitions.ts | 3 +- yarn.lock | 24 ++---- 10 files changed, 180 insertions(+), 109 deletions(-) create mode 100644 packages/debug-adapter/src/target-finders.ts create mode 100644 packages/debug-adapter/src/utils/path-converters.ts diff --git a/package.json b/package.json index 621691b..87b9a73 100644 --- a/package.json +++ b/package.json @@ -96,12 +96,10 @@ "languages": [ "wollok" ], - "program": "./out/debug-adapter/src/index.js", "runtime": "node", "configurationAttributes": { "launch": { "required": [ - "file", "target" ], "properties": { @@ -109,25 +107,42 @@ "type": "object", "description": "Program or test to run.", "properties": { + "type": { + "type": "string", + "description": "The type of targetting.", + "enum": [ + "program", + "test", + "fqn" + ], + "enumDescriptions": [ + "Target a program, should specify program and file", + "Target a test, should specify test, describe and file", + "Target a fully qualified name, should specify fqn" + ] + }, "program": { "type": "string", "description": "The program's name." }, "describe": { "type": "string", - "description": "(optional) The describe's name.", - "optional": true + "description": "(optional) The describe's name." }, "test": { "type": "string", "description": "The test's name." + }, + "fqn": { + "type": "string", + "description": "The fully qualified name of the describe/test/program you want to run." + }, + "file": { + "type": "string", + "description": "Absolute path to a Wollok file." } } }, - "file": { - "type": "string", - "description": "Absolute path to a Wollok file." - }, "stopOnEntry": { "type": "boolean", "description": "Stop on the first line of the program/test.", @@ -343,7 +358,7 @@ "lint-staged": "lint-staged" }, "dependencies": { - "wollok-ts": "4.1.8" + "wollok-ts": "../wollok-ts" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/packages/client/src/commands.ts b/packages/client/src/commands.ts index 44c1fc1..bc5aebf 100644 --- a/packages/client/src/commands.ts +++ b/packages/client/src/commands.ts @@ -14,7 +14,7 @@ import { asShellString, fsToShell, } from './platform-string-utils' -import { COMMAND_RUN_ALL_TESTS, COMMAND_RUN_GAME, COMMAND_RUN_PROGRAM, COMMAND_RUN_TEST, COMMAND_START_REPL, wollokLSPExtensionCode, COMMAND_INIT_PROJECT } from './shared-definitions' +import { COMMAND_RUN_ALL_TESTS, COMMAND_RUN_GAME, COMMAND_RUN_PROGRAM, COMMAND_RUN_TEST, COMMAND_START_REPL, wollokLSPExtensionCode, COMMAND_INIT_PROJECT, COMMAND_DEBUG } from './shared-definitions' import { getLSPMessage } from './messages' export const subscribeWollokCommands = (context: ExtensionContext): void => { @@ -32,6 +32,7 @@ export const subscribeWollokCommands = (context: ExtensionContext): void => { context.subscriptions.push( registerCLICommand(COMMAND_INIT_PROJECT, initProject), ) + context.subscriptions.push(registerDebuggerCommand()) } /** @@ -141,3 +142,20 @@ const wollokCLITask = (task: string, name: string, cliCommands: Array { + vscode.debug.startDebugging( + vscode.workspace.workspaceFolders[0], + { + type: "wollok", + request: "launch", + name: "Launch Debug Session", + stopOnEntry: false, + target: { + type: 'fqn', + fqn, + }, + } + ) + }) +} diff --git a/packages/client/src/shared-definitions.ts b/packages/client/src/shared-definitions.ts index 7dac2dc..72c9a17 100644 --- a/packages/client/src/shared-definitions.ts +++ b/packages/client/src/shared-definitions.ts @@ -10,4 +10,6 @@ export const COMMAND_RUN_GAME = 'wollok.run.game' export const COMMAND_RUN_PROGRAM = 'wollok.run.program' export const COMMAND_RUN_ALL_TESTS = 'wollok.run.allTests' export const COMMAND_RUN_TEST = 'wollok.run.test' -export const COMMAND_INIT_PROJECT = 'wollok.init.project' \ No newline at end of file +export const COMMAND_INIT_PROJECT = 'wollok.init.project' +//ToDo: Create shared package +export const COMMAND_DEBUG = 'wollok.debug' \ No newline at end of file diff --git a/packages/debug-adapter/src/debug-configuration-provider.ts b/packages/debug-adapter/src/debug-configuration-provider.ts index 7401a6c..2e4a9ca 100644 --- a/packages/debug-adapter/src/debug-configuration-provider.ts +++ b/packages/debug-adapter/src/debug-configuration-provider.ts @@ -4,23 +4,7 @@ import * as vscode from 'vscode' export class WollokDebugConfigurationProvider implements DebugConfigurationProvider { resolveDebugConfiguration(_folder: WorkspaceFolder | undefined, config: DebugConfiguration, _token?: CancellationToken): ProviderResult { - if (!config.file) { - return vscode.window.showErrorMessage("Cannot find a file to debug").then(_ => { - return undefined // abort launch - }) - } - - if(config.target.program && config.target.test){ - return vscode.window.showErrorMessage("Cannot specify both program and test properties at the same time").then(_ => { - return undefined // abort launch - }) - } - - if(!config.target.program && !config.target.test) { - return vscode.window.showErrorMessage("Must specify either program or test property").then(_ => { - return undefined // abort launch - }) - } + //ToDo validations? return config } diff --git a/packages/debug-adapter/src/debug-session.ts b/packages/debug-adapter/src/debug-session.ts index 4457004..a96fcb3 100644 --- a/packages/debug-adapter/src/debug-session.ts +++ b/packages/debug-adapter/src/debug-session.ts @@ -1,12 +1,12 @@ import { DebugSession, InitializedEvent, OutputEvent, Source, StackFrame, StoppedEvent, TerminatedEvent, Thread, Variable } from '@vscode/debugadapter' import { DebugProtocol } from '@vscode/debugprotocol' -import path = require('path') import * as vscode from 'vscode' -import { Body, BOOLEAN_MODULE, buildEnvironment, Context, DirectedInterpreter, ExecutionDirector, executionFor, ExecutionState, FileContent, Frame, interprete, is, LIST_MODULE, Node, NUMBER_MODULE, Package, Program, PROGRAM_FILE_EXTENSION, RuntimeObject, RuntimeValue, Sentence, STRING_MODULE, Test, TEST_FILE_EXTENSION, WOLLOK_FILE_EXTENSION, Interpreter } from 'wollok-ts' +import { Body, BOOLEAN_MODULE, buildEnvironment, Context, DirectedInterpreter, ExecutionDirector, executionFor, ExecutionState, FileContent, Frame, interprete, is, LIST_MODULE, Node, NUMBER_MODULE, Package, Program, PROGRAM_FILE_EXTENSION, RuntimeObject, RuntimeValue, Sentence, STRING_MODULE, Test, TEST_FILE_EXTENSION, WOLLOK_FILE_EXTENSION, Interpreter, Describe } from 'wollok-ts' +import { LaunchTargetArguments, Target, targetFinder } from './target-finders' +import { toClientPath, toWollokPath } from './utils/path-converters' import { WollokPositionConverter } from './utils/wollok-position-converter' export class WollokDebugSession extends DebugSession { protected static readonly THREAD_ID = 1 - protected static WOLLOK_PATH_SEPARATOR = '/' protected interpreter: DirectedInterpreter protected executionDirector: ExecutionDirector @@ -49,7 +49,7 @@ export class WollokDebugSession extends DebugSession { const wollokPackages = await Promise.all(files.map(file => new Promise(resolve => this.workspace.openTextDocument(file).then(textDocument => { - resolve({ name: this.toWollokPath(textDocument.uri.fsPath), content: textDocument.getText() }) + resolve({ name: toWollokPath(textDocument.uri.fsPath), content: textDocument.getText() }) })) )) @@ -61,28 +61,11 @@ export class WollokDebugSession extends DebugSession { } protected launchRequest(response: DebugProtocol.LaunchResponse, args: WollokLaunchArguments, _request?: DebugProtocol.Request): void { - const containerPackage = this.interpreter.evaluation.environment.descendants.filter(is(Package)).find(pkg => pkg.sourceFileName === this.toWollokPath(args.file)) + let container: Target - if(!containerPackage){ - this.sendErrorResponse(response, 404, 'Could not find target file') - return - } - - const container: Test | Program | undefined = containerPackage.descendants.find(function (node: Node): node is Test | Program { - if('test' in args.target) { - const isPossibleTargetTest = node.is(Test) && node.name === `"${args.target.test}"` - if(args.target.describe) { - // possible bug: recursive describes? - return isPossibleTargetTest && node.parent.name === `"${args.target.describe}"` - } else { - return isPossibleTargetTest - } - } else { - return node.is(Program) && node.name === args.target.program - } - }) - - if(!container){ + try { + container = targetFinder(args.target).findTarget(this.interpreter.evaluation.environment) + } catch(_error) { this.sendErrorResponse(response, 404, 'Could not find target test or program') return } @@ -100,14 +83,12 @@ export class WollokDebugSession extends DebugSession { this.sendResponse(response) this.moveExecution(() => { return this.executionDirector.resume( - args.stopOnEntry ? node => container.body.sentences[0]?.id === node.id : undefined + args.stopOnEntry ? node => (container as any).body.sentences[0]?.id === node.id : undefined ) }, args.stopOnEntry ? 'entry' : undefined) }) } - protected threadsRequest(response: DebugProtocol.ThreadsResponse): void { - // runtime supports no threads so just return a default thread. response.body = { threads: [ @@ -272,14 +253,6 @@ export class WollokDebugSession extends DebugSession { this.sendResponse(response) } - protected toWollokPath(aPath: string): string { - return aPath.replace(new RegExp( '\\' + path.sep, 'g'), WollokDebugSession.WOLLOK_PATH_SEPARATOR) - } - - protected toClientPath(aWollokPath: string): string { - return aWollokPath.replace(new RegExp( '\\' + WollokDebugSession.WOLLOK_PATH_SEPARATOR, 'g'), path.sep) - } - protected variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments, _request?: DebugProtocol.Request): void { const variables: DebugProtocol.VariablesResponse['body']['variables'] = [] const context = this.contexts.get(args.variablesReference) @@ -324,11 +297,11 @@ export class WollokDebugSession extends DebugSession { } private sourceFromNode(node: T): Source { - return new Source(node.sourceFileName.split('/').pop()!, this.toClientPath(node.sourceFileName)) + return new Source(node.sourceFileName.split('/').pop()!, toClientPath(node.sourceFileName)) } private packageFromSource(source: Source): Package { - const pkg = this.interpreter.evaluation.environment.descendants.find(node => node.is(Package) && this.toWollokPath(source.path) === node.sourceFileName) as Package | undefined + const pkg = this.interpreter.evaluation.environment.descendants.find(node => node.is(Package) && toWollokPath(source.path) === node.sourceFileName) as Package | undefined if(!pkg) { throw new Error(`Could not find package for source ${source.path}`) } @@ -357,11 +330,7 @@ function getLabel(value: RuntimeObject): string { interface WollokLaunchArguments extends DebugProtocol.LaunchRequestArguments { stopOnEntry?: boolean - file: string, - target: { - test: string, - describe?: string - } | { program: string } + target: LaunchTargetArguments } class WollokIdMap extends Map { diff --git a/packages/debug-adapter/src/target-finders.ts b/packages/debug-adapter/src/target-finders.ts new file mode 100644 index 0000000..564a8d3 --- /dev/null +++ b/packages/debug-adapter/src/target-finders.ts @@ -0,0 +1,76 @@ +import { Describe, Environment, Node, Package, Program, Test } from 'wollok-ts' +import { toWollokPath } from './utils/path-converters' + +export type LaunchTargetArguments = + { type: 'test', file: string, test: string, describe?: string } | + { type: 'program', program: string, file: string, } | + { type: 'fqn', fqn: string } + +export type Target = Program | Test + +export function targetFinder(type: LaunchTargetArguments): TargetFinder { + switch(type.type) { + case 'test': return new TestTargetFinder(type.file, type.test, type.describe) + case 'program': return new ProgramTargetFinder(type.file, type.program) + case 'fqn': return new FqnTargetFinder(type.fqn) + } +} + +abstract class TargetFinder { + findTarget(environment: Environment): Target { + const container = this.findTargetOrUndefined(environment) + if(!container) throw new Error('Container not found') + return container + } + + abstract findTargetOrUndefined(environment: Environment): Target | undefined +} + +class FqnTargetFinder extends TargetFinder { + constructor(private fqn: string) { + super() + } + + findTargetOrUndefined(environment: Environment): Target | undefined { + return environment.descendants.find(node => + (node.is(Program) || node.is(Test)) && + node.fullyQualifiedName.endsWith(this.fqn) + ) as Program | Test | undefined + } +} + +abstract class FileTargetFinder extends TargetFinder{ + private path: string + constructor(clientPath: string) { + super() + this.path = toWollokPath(clientPath) + } + + findTargetOrUndefined(environment: Environment): Target | undefined { + const pkg = environment.descendants.find(node => node.is(Package) && this.path === node.sourceFileName) as Package | undefined + if(!pkg) return undefined + return pkg.descendants.find(node => this.isValidTarget(node)) as Target | undefined + } + + abstract isValidTarget(node: Node): boolean +} + +class ProgramTargetFinder extends FileTargetFinder { + constructor(filePath: string, private program: string){ + super(filePath) + } + + isValidTarget(node: Node): boolean { + return node.is(Program) && node.name === this.program + } +} + +class TestTargetFinder extends FileTargetFinder { + constructor(filePath: string, private test: string, private describe?: string){ + super(filePath) + } + + isValidTarget(node: Node): boolean { + return node.is(Test) && node.name === `"${this.test}"` && (!this.describe || node.parent?.is(Describe) && node.parent.name === `"${this.describe}"`) + } +} \ No newline at end of file diff --git a/packages/debug-adapter/src/utils/path-converters.ts b/packages/debug-adapter/src/utils/path-converters.ts new file mode 100644 index 0000000..d19302a --- /dev/null +++ b/packages/debug-adapter/src/utils/path-converters.ts @@ -0,0 +1,10 @@ +import path = require('path') + +const WOLLOK_PATH_SEPARATOR = '/' +export const toWollokPath = (aPath: string): string => { + return aPath.replace(new RegExp( '\\' + path.sep, 'g'), WOLLOK_PATH_SEPARATOR) +} + +export const toClientPath = (aWollokPath: string): string => { + return aWollokPath.replace(new RegExp( '\\' + WOLLOK_PATH_SEPARATOR, 'g'), path.sep) +} \ No newline at end of file diff --git a/packages/server/src/functionalities/code-lens.ts b/packages/server/src/functionalities/code-lens.ts index 9375846..0837e45 100644 --- a/packages/server/src/functionalities/code-lens.ts +++ b/packages/server/src/functionalities/code-lens.ts @@ -2,7 +2,7 @@ import { CodeLens, CodeLensParams, Position, Range } from 'vscode-languageserver import { Describe, Node, Package, Test, Program, Environment, is, PROGRAM_FILE_EXTENSION, TEST_FILE_EXTENSION, WOLLOK_FILE_EXTENSION, Class, Singleton, Entity } from 'wollok-ts' import { getWollokFileExtension, packageFromURI, toVSCRange } from '../utils/text-documents' import { removeQuotes } from '../utils/strings' -import { COMMAND_RUN_GAME, COMMAND_RUN_PROGRAM, COMMAND_RUN_TEST, COMMAND_START_REPL } from '../shared-definitions' +import { COMMAND_DEBUG, COMMAND_RUN_GAME, COMMAND_RUN_PROGRAM, COMMAND_RUN_TEST, COMMAND_START_REPL } from '../shared-definitions' export const codeLenses = (environment: Environment) => (params: CodeLensParams): CodeLens[] | null => { const fileExtension = getWollokFileExtension(params.textDocument.uri) @@ -25,6 +25,7 @@ export const getProgramCodeLenses = (file: Package): CodeLens[] => file.members.filter(is(Program)).flatMap(program => [ buildLens(program, COMMAND_RUN_GAME, 'Run game'), buildLens(program, COMMAND_RUN_PROGRAM, 'Run program'), + buildLens(program, COMMAND_DEBUG, 'Debug program'), ]) @@ -32,12 +33,11 @@ export const getTestCodeLenses = (file: Package): CodeLens[] => { const runAllTests = buildRunAllTestsCodeLens(file) return [ - runAllTests - , + runAllTests, ...file .descendants .filter(isTesteable) - .map(node => buildTestCodeLens(file, node)), + .flatMap(node => buildTestCodeLens(file, node)), ] } @@ -57,7 +57,7 @@ const isWollokDefinition = (node: Node): node is Class | Singleton => node.is(Class) || node.is(Singleton) const buildRunAllTestsCodeLens = (file: Package): CodeLens => - buildTestsCodeLens( + buildCommandCodeLens( Range.create(Position.create(0, 0), Position.create(0, 0)), 'wollok.run.test', 'Run all tests', @@ -65,24 +65,34 @@ const buildRunAllTestsCodeLens = (file: Package): CodeLens => ) -const buildTestCodeLens = (file: Package, node: Test | Describe): CodeLens => { +const buildTestCodeLens = (file: Package, node: Test | Describe): CodeLens[] => { const describe = node.is(Describe) ? node.name : node.parent?.is(Describe) ? node.parent.name : null const test = node.is(Test) ? node.name : null - return buildTestsCodeLens( - toVSCRange(node.sourceMap!), - COMMAND_RUN_TEST, - `Run ${node.is(Test) ? 'test' : 'describe'}`, - [ - null, - file.fileName!, - describe ? removeQuotes(describe) : null, - test ? removeQuotes(test) : null, - ] - ) + return [ + buildCommandCodeLens( + toVSCRange(node.sourceMap!), + COMMAND_RUN_TEST, + `Run ${node.is(Test) ? 'test' : 'describe'}`, + [ + null, + file.fileName!, + describe ? removeQuotes(describe) : null, + test ? removeQuotes(test) : null, + ] + ), + buildCommandCodeLens( + toVSCRange(node.sourceMap!), + COMMAND_DEBUG, + `Debug ${node.is(Test) ? 'test' : 'describe'}`, + [ + node.fullyQualifiedName, + ] + ), + ] } -const buildTestsCodeLens = (range: Range, command: string, title: string, args: [string|null, string|null, string|null, string|null]): CodeLens => ({ +const buildCommandCodeLens = (range: Range, command: string, title: string, args: Array): CodeLens => ({ range, command: { command, diff --git a/packages/server/src/shared-definitions.ts b/packages/server/src/shared-definitions.ts index dca7924..37f413b 100644 --- a/packages/server/src/shared-definitions.ts +++ b/packages/server/src/shared-definitions.ts @@ -9,4 +9,5 @@ export const COMMAND_START_REPL = 'wollok.start.repl' export const COMMAND_RUN_GAME = 'wollok.run.game' export const COMMAND_RUN_PROGRAM = 'wollok.run.program' export const COMMAND_RUN_ALL_TESTS = 'wollok.run.allTests' -export const COMMAND_RUN_TEST = 'wollok.run.test' \ No newline at end of file +export const COMMAND_RUN_TEST = 'wollok.run.test' +export const COMMAND_DEBUG = 'wollok.debug' \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 459e948..e721532 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8867,7 +8867,6 @@ __metadata: mocha: "npm:^10.7.3" nyc: "npm:^17.0.0" ts-mocha: "npm:^10.0.0" - wollok-ts: "npm:4.1.6" languageName: unknown linkType: soft @@ -8930,34 +8929,21 @@ __metadata: source-map-support: "npm:^0.5.21" ts-node: "npm:^10.9.1" typescript: "npm:^4.9.5" - wollok-ts: "npm:4.1.8" + wollok-ts: ../wollok-ts yarn-run-all: "npm:^3.1.1" languageName: unknown linkType: soft -"wollok-ts@npm:4.1.6": - version: 4.1.6 - resolution: "wollok-ts@npm:4.1.6" - dependencies: - "@types/parsimmon": "npm:^1.10.8" - parsimmon: "npm:^1.18.1" - prettier-printer: "npm:^1.1.4" - unraw: "npm:^3.0.0" - uuid: "npm:^9.0.1" - checksum: 10c0/2248c12858540699ce9c58b6e22255d2dd685ef0ae1c32ca023cc0a7c9d8489057a5b0600c0291e4f45cb831dc4c7ccd43de73b4c041b8569300d66c0c7bbbcf - languageName: node - linkType: hard - -"wollok-ts@npm:4.1.8": - version: 4.1.8 - resolution: "wollok-ts@npm:4.1.8" +"wollok-ts@file:../wollok-ts::locator=wollok-lsp-ide%40workspace%3A.": + version: 4.1.9 + resolution: "wollok-ts@file:../wollok-ts#../wollok-ts::hash=65aaf1&locator=wollok-lsp-ide%40workspace%3A." dependencies: "@types/parsimmon": "npm:^1.10.8" parsimmon: "npm:^1.18.1" prettier-printer: "npm:^1.1.4" unraw: "npm:^3.0.0" uuid: "npm:^9.0.1" - checksum: 10c0/57656102b6bfc7d4c3b84a6c27038fd8b6a9708c0e8a18526cdcb9e369ca229639503f7d26ef720c15644ab9230f019a422b39837e1bee90fa911afbd1a1b90d + checksum: 10c0/b5c7cda3bb8da916ba812d7df5244fc6ecba15813f566d685bcb470ac4a46e003a2d170b95aa56235dd72c1853092661cfefb817751120559c5f36ffd4dde4f5 languageName: node linkType: hard From e6beec04b9fba5acf0355a2929ba1cdb6502f04f Mon Sep 17 00:00:00 2001 From: ivojawer Date: Mon, 2 Dec 2024 19:30:00 -0300 Subject: [PATCH 08/13] finish testing target finders --- .../src/test/debug-adapter.test.ts | 12 ++-- .../src/test/target-finder.test.ts | 61 ++++++++++++++++++- 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/packages/debug-adapter/src/test/debug-adapter.test.ts b/packages/debug-adapter/src/test/debug-adapter.test.ts index 279fc7b..2d72d53 100644 --- a/packages/debug-adapter/src/test/debug-adapter.test.ts +++ b/packages/debug-adapter/src/test/debug-adapter.test.ts @@ -186,8 +186,10 @@ describe('debug adapter', function () { }), dc.configurationDoneRequest(), ]) - await dc.assertOutput('stdout', "Finished executing without errors", 1000) - await dc.waitForEvent('terminated', 1000) + await Promise.all([ + dc.assertOutput('stdout', "Finished executing without errors", 1000), + dc.waitForEvent('terminated', 1000), + ]) }) it('finishing with errors', async function (){ @@ -203,8 +205,10 @@ describe('debug adapter', function () { }), dc.configurationDoneRequest(), ]) - await dc.assertOutput('stderr', "My exception message", 3000) - await dc.waitForEvent('terminated', 3000) + await Promise.all([ + dc.assertOutput('stderr', "My exception message", 1000), + dc.waitForEvent('terminated', 1000), + ]) }) }) }) diff --git a/packages/debug-adapter/src/test/target-finder.test.ts b/packages/debug-adapter/src/test/target-finder.test.ts index 21b04d7..4c8aef8 100644 --- a/packages/debug-adapter/src/test/target-finder.test.ts +++ b/packages/debug-adapter/src/test/target-finder.test.ts @@ -1,5 +1,23 @@ import { FqnTargetFinder, LaunchTargetArguments, ProgramTargetFinder, targetFinder, TestTargetFinder } from '../target-finders' import * as assert from 'node:assert' +import { buildEnvironment, Environment } from 'wollok-ts' + +const PROGRAM_PATH = '/users/user/documents/my-project/a/pepitaProgram.wpgm' +const TEST_PATH = '/users/user/documents/my-project/a/pepitaTests.wtest' +const program = ` +program pepitaRun { + pepita.fly(10) +} +` + +const test = ` +describe "pepita tests" { + test "pepita can fly" { + pepita.fly(10) + } +} +` + describe('target finder', () => { const fqnTargetConfiguration: LaunchTargetArguments = { @@ -8,7 +26,7 @@ describe('target finder', () => { } const testTargetConfiguration: LaunchTargetArguments = { type: 'test', - file: './pepitaTests', + file: TEST_PATH, test: 'pepita can fly', describe: 'pepita tests', } @@ -16,7 +34,7 @@ describe('target finder', () => { const programTargetConfiguration: LaunchTargetArguments = { type: 'program', program: 'pepitaRun', - file: './pepitaProgram', + file: PROGRAM_PATH, } describe('selects the correct target finder', () => { @@ -35,4 +53,43 @@ describe('target finder', () => { assert(finder instanceof ProgramTargetFinder) }) }) + + describe('finds the target', () => { + let environment: Environment + beforeEach(() => { + environment = buildEnvironment([ + { + content: program, + name: PROGRAM_PATH, + }, + { + content: test, + name: TEST_PATH, + }, + ]) + }) + + it('should throw an error when the target is not found', () => { + const finder = targetFinder({ type: 'fqn', fqn: 'some.wrong.fqn' }) + assert.throws(() => finder.findTarget(environment)) + }) + + it('fqn target finder', () => { + const finder = targetFinder(fqnTargetConfiguration) + const target = finder.findTarget(environment) + assert.equal(target.name, '"pepita can fly"') + }) + + it('test target finder', () => { + const finder = targetFinder(testTargetConfiguration) + const target = finder.findTarget(environment) + assert.equal(target.name, '"pepita can fly"') + }) + + it('program target finder', () => { + const finder = targetFinder(programTargetConfiguration) + const target = finder.findTarget(environment) + assert.equal(target.name, 'pepitaRun') + }) + }) }) \ No newline at end of file From 6c6f36502ee5acd727ae73f7243cbfb73a308ef3 Mon Sep 17 00:00:00 2001 From: ivojawer Date: Mon, 2 Dec 2024 19:38:31 -0300 Subject: [PATCH 09/13] validating configuration + remove describe code lens --- .../src/debug-configuration-provider.ts | 19 ++++++++++++++++++- .../server/src/functionalities/code-lens.ts | 4 ++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/packages/debug-adapter/src/debug-configuration-provider.ts b/packages/debug-adapter/src/debug-configuration-provider.ts index 2e4a9ca..4c06dfb 100644 --- a/packages/debug-adapter/src/debug-configuration-provider.ts +++ b/packages/debug-adapter/src/debug-configuration-provider.ts @@ -1,10 +1,27 @@ import { CancellationToken, DebugConfiguration, DebugConfigurationProvider, ProviderResult, WorkspaceFolder } from 'vscode' import * as vscode from 'vscode' +const targetTypeRequiredKeys = { + 'fqn': ['fqn'], + 'program': ['file', 'program'], + 'test': ['file', 'test'], +} + export class WollokDebugConfigurationProvider implements DebugConfigurationProvider { resolveDebugConfiguration(_folder: WorkspaceFolder | undefined, config: DebugConfiguration, _token?: CancellationToken): ProviderResult { - //ToDo validations? + if(!config.target.type) { + return vscode.window.showErrorMessage('No target type provided').then(() => undefined) + } + + if(!targetTypeRequiredKeys[config.target.type]) { + return vscode.window.showErrorMessage(`Unknown target type: ${config.target.type}`).then(() => undefined) + } + + const missingKeys = targetTypeRequiredKeys[config.target.type].filter(key => !config.target[key]) + if(missingKeys.length) { + return vscode.window.showErrorMessage(`Missing required target parameters: ${missingKeys.join(', ')}`).then(() => undefined) + } return config } diff --git a/packages/server/src/functionalities/code-lens.ts b/packages/server/src/functionalities/code-lens.ts index bc2d064..a78be9a 100644 --- a/packages/server/src/functionalities/code-lens.ts +++ b/packages/server/src/functionalities/code-lens.ts @@ -82,14 +82,14 @@ const buildTestCodeLens = (file: Package, node: Test | Describe): CodeLens[] => test ? removeQuotes(test) : null, ] ), - buildCommandCodeLens( + ...node.is(Test) ? [buildCommandCodeLens( toVSCRange(node.sourceMap!), COMMAND_DEBUG, getLSPMessage(COMMAND_EXECUTE_DEBUG, [messageDescription]), [ node.fullyQualifiedName, ] - ), + )] : [], ] } From e5d58d1ea7f7eff2aac7325e5a959d00afa15e42 Mon Sep 17 00:00:00 2001 From: ivojawer Date: Wed, 15 Jan 2025 03:01:13 -0300 Subject: [PATCH 10/13] codelens test --- packages/client/src/test/code-lens.test.ts | 16 ++++++++++++++++ packages/debug-adapter/src/debug-session.ts | 2 +- packages/server/src/functionalities/code-lens.ts | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/client/src/test/code-lens.test.ts b/packages/client/src/test/code-lens.test.ts index b20885c..d6f4617 100644 --- a/packages/client/src/test/code-lens.test.ts +++ b/packages/client/src/test/code-lens.test.ts @@ -23,6 +23,14 @@ suite('Should do code lenses', () => { arguments: ['pepitaGame.juego'], }, ), + new CodeLens( + new Range(new Position(0, 0), new Position(2, 1)), + { + title: 'Debug program', + command: 'wollok.debug', + arguments: ['pepitaGame.juego'], + } + ), ]) }) @@ -52,6 +60,14 @@ suite('Should do code lenses', () => { arguments: [null, 'test.wtest', 'pepita test', 'pepita is happy'], }, ), + new CodeLens( + new Range(new Position(1, 4), new Position(3, 5)), + { + title: 'Debug test', + command: 'wollok.debug', + arguments: ['test."pepita test"."pepita is happy"'], + } + ), ]) }) diff --git a/packages/debug-adapter/src/debug-session.ts b/packages/debug-adapter/src/debug-session.ts index a96fcb3..739715e 100644 --- a/packages/debug-adapter/src/debug-session.ts +++ b/packages/debug-adapter/src/debug-session.ts @@ -1,7 +1,7 @@ import { DebugSession, InitializedEvent, OutputEvent, Source, StackFrame, StoppedEvent, TerminatedEvent, Thread, Variable } from '@vscode/debugadapter' import { DebugProtocol } from '@vscode/debugprotocol' import * as vscode from 'vscode' -import { Body, BOOLEAN_MODULE, buildEnvironment, Context, DirectedInterpreter, ExecutionDirector, executionFor, ExecutionState, FileContent, Frame, interprete, is, LIST_MODULE, Node, NUMBER_MODULE, Package, Program, PROGRAM_FILE_EXTENSION, RuntimeObject, RuntimeValue, Sentence, STRING_MODULE, Test, TEST_FILE_EXTENSION, WOLLOK_FILE_EXTENSION, Interpreter, Describe } from 'wollok-ts' +import { Body, BOOLEAN_MODULE, buildEnvironment, Context, DirectedInterpreter, ExecutionDirector, executionFor, ExecutionState, FileContent, Frame, interprete, LIST_MODULE, Node, NUMBER_MODULE, Package, PROGRAM_FILE_EXTENSION, RuntimeObject, RuntimeValue, Sentence, STRING_MODULE, TEST_FILE_EXTENSION, WOLLOK_FILE_EXTENSION, Interpreter } from 'wollok-ts' import { LaunchTargetArguments, Target, targetFinder } from './target-finders' import { toClientPath, toWollokPath } from './utils/path-converters' import { WollokPositionConverter } from './utils/wollok-position-converter' diff --git a/packages/server/src/functionalities/code-lens.ts b/packages/server/src/functionalities/code-lens.ts index a78be9a..3c26f96 100644 --- a/packages/server/src/functionalities/code-lens.ts +++ b/packages/server/src/functionalities/code-lens.ts @@ -26,7 +26,7 @@ export const getProgramCodeLenses = (file: Package): CodeLens[] => file.members.filter(is(Program)).flatMap(program => [ buildLens(program, COMMAND_RUN_GAME, getLSPMessage(COMMAND_RUN_GAME)), buildLens(program, COMMAND_RUN_PROGRAM, getLSPMessage(COMMAND_RUN_PROGRAM)), - buildLens(program, COMMAND_DEBUG, getLSPMessage(COMMAND_DEBUG)), + buildLens(program, COMMAND_DEBUG, getLSPMessage(COMMAND_EXECUTE_DEBUG, ['program'])), ]) From 5539313312299021bf70646a737994b752eae223 Mon Sep 17 00:00:00 2001 From: Publishing Bot Date: Wed, 15 Jan 2025 06:16:50 +0000 Subject: [PATCH 11/13] =?UTF-8?q?=F0=9F=93=9D=20Update=20contributors=20li?= =?UTF-8?q?st?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8101d19..ee6e9fa 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ - + # Wollok IDE @@ -40,6 +40,6 @@ Do you want to contribute? Great, you are always welcome! ## 👥 Contributors -ivojawer fdodino PalumboN npasserini Miranda-03 dependabot[bot] FerRomMu  +ivojawer fdodino PalumboN npasserini dependabot[bot] Miranda-03 FerRomMu  From b0e8e54000c143e4847919c22fdbbff3a7ecf086 Mon Sep 17 00:00:00 2001 From: Ivan Jawerbaum Date: Wed, 15 Jan 2025 13:09:11 -0300 Subject: [PATCH 12/13] Update packages/debug-adapter/src/debug-session.ts Co-authored-by: Nahuel Palumbo --- packages/debug-adapter/src/debug-session.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/debug-adapter/src/debug-session.ts b/packages/debug-adapter/src/debug-session.ts index 739715e..35a7086 100644 --- a/packages/debug-adapter/src/debug-session.ts +++ b/packages/debug-adapter/src/debug-session.ts @@ -53,7 +53,7 @@ export class WollokDebugSession extends DebugSession { })) )) - const environment = buildEnvironment(wollokPackages, undefined) + const environment = buildEnvironment(projectFiles) this.interpreter = executionFor(environment) this.sendResponse(response) this.sendEvent(new InitializedEvent()) From 025145b08a2801df91dec6eddadd04d7e920016f Mon Sep 17 00:00:00 2001 From: ivojawer Date: Wed, 15 Jan 2025 14:29:59 -0300 Subject: [PATCH 13/13] refactors --- packages/debug-adapter/package.json | 2 + packages/debug-adapter/src/debug-session.ts | 43 +++++------ .../src/test/debug-adapter.test.ts | 13 +--- yarn.lock | 74 ++++++++++++++++++- 4 files changed, 101 insertions(+), 31 deletions(-) diff --git a/packages/debug-adapter/package.json b/packages/debug-adapter/package.json index 299e5e3..a0db618 100644 --- a/packages/debug-adapter/package.json +++ b/packages/debug-adapter/package.json @@ -17,10 +17,12 @@ "@vscode/debugprotocol": "^1.66.0" }, "devDependencies": { + "@types/chai": "^4", "@types/mocha": "^10", "@types/node": "^18.14.1", "@types/vscode": "^1.92.0", "@vscode/debugadapter-testsupport": "^1.67.0", + "chai": "4.3.10", "mocha": "^10.7.3", "nyc": "^17.0.0", "ts-mocha": "^10.0.0" diff --git a/packages/debug-adapter/src/debug-session.ts b/packages/debug-adapter/src/debug-session.ts index 35a7086..de9c13a 100644 --- a/packages/debug-adapter/src/debug-session.ts +++ b/packages/debug-adapter/src/debug-session.ts @@ -46,7 +46,7 @@ export class WollokDebugSession extends DebugSession { // initialize wollok interpreter const debuggableFileExtensions = [WOLLOK_FILE_EXTENSION, PROGRAM_FILE_EXTENSION, TEST_FILE_EXTENSION] this.workspace.findFiles(`**/*.{${debuggableFileExtensions.join(',')}}`).then(async files => { - const wollokPackages = await Promise.all(files.map(file => + const projectFiles = await Promise.all(files.map(file => new Promise(resolve => this.workspace.openTextDocument(file).then(textDocument => { resolve({ name: toWollokPath(textDocument.uri.fsPath), content: textDocument.getText() }) @@ -101,6 +101,7 @@ export class WollokDebugSession extends DebugSession { protected setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments, _request?: DebugProtocol.Request): void { const breakpointsPackage = this.packageFromSource(args.source as Source) + // Remove old breakpoints from the requested file const breakpointsToRemove = [] this.executionDirector.breakpoints.forEach(breakpointedNode => { if(breakpointedNode.parentPackage.id === breakpointsPackage.id) { @@ -109,27 +110,27 @@ export class WollokDebugSession extends DebugSession { }) breakpointsToRemove.forEach(breapointedNode => this.executionDirector.removeBreakpoint(breapointedNode)) - if(breakpointsPackage) { - const nodesToBreakAt = breakpointsPackage.descendants.filter((node: Node) => { - return args.breakpoints.some(breakpoint => - breakpoint.column ? - this.positionConverter.convertDebuggerLineToClient(node.sourceMap?.start.line) === breakpoint.line && this.positionConverter.convertDebuggerColumnToClient(node.sourceMap?.start.column) === breakpoint.column : - node.is(Sentence) && node.parent.is(Body) && this.positionConverter.convertDebuggerLineToClient(node.sourceMap?.start.line) === breakpoint.line - ) - }) - nodesToBreakAt.forEach(node => { - this.executionDirector.addBreakpoint(node) - }) - - response.body = { - breakpoints: nodesToBreakAt.map(node => ({ - verified: true, - ...this.positionConverter.convertSourceMapToClient(node.sourceMap), - source: this.sourceFromNode(node), - }) - ), + // Add all breakpoints to the requested file + const nodesToBreakAt = breakpointsPackage.descendants.filter((node: Node) => { + return args.breakpoints.some(breakpoint => + breakpoint.column ? + // if the breakpoint has a column then it must be an inline breakpoint + this.positionConverter.convertDebuggerLineToClient(node.sourceMap?.start.line) === breakpoint.line && this.positionConverter.convertDebuggerColumnToClient(node.sourceMap?.start.column) === breakpoint.column : + // otherwise look for a sentence + node.is(Sentence) && node.parent.is(Body) && this.positionConverter.convertDebuggerLineToClient(node.sourceMap?.start.line) === breakpoint.line + ) + }) + nodesToBreakAt.forEach(node => { + this.executionDirector.addBreakpoint(node) + }) - } + response.body = { + breakpoints: nodesToBreakAt.map(node => ({ + verified: true, + ...this.positionConverter.convertSourceMapToClient(node.sourceMap), + source: this.sourceFromNode(node), + }) + ), } this.sendResponse(response) } diff --git a/packages/debug-adapter/src/test/debug-adapter.test.ts b/packages/debug-adapter/src/test/debug-adapter.test.ts index 54cbbee..acaa656 100644 --- a/packages/debug-adapter/src/test/debug-adapter.test.ts +++ b/packages/debug-adapter/src/test/debug-adapter.test.ts @@ -1,14 +1,14 @@ import * as path from 'path' import { DebugClient } from '@vscode/debugadapter-testsupport' -import * as assert from 'node:assert' import { DebugProtocol } from '@vscode/debugprotocol' - +import { assert } from 'chai' const DEBUG_ADAPTER = path.resolve(__dirname, 'start-debug-session.js') const FIXTURES_ROOT = path.resolve(__dirname, '../../../../packages/debug-adapter/src/test/fixtures') const PROGRAM = path.resolve(FIXTURES_ROOT, 'aProgram.wpgm') const TEST_FILE = path.resolve(FIXTURES_ROOT, 'aTest.wtest') const WLK = path.resolve(FIXTURES_ROOT, 'anObject.wlk') + describe('debug adapter', function () { let dc: DebugClient @@ -20,7 +20,7 @@ describe('debug adapter', function () { this.afterEach( function () { return dc.stop() }) it('unknown request should produce error', function () { - return assert.rejects(dc.send('illegal_request')) + dc.send('illegal_request').then(() => assert.fail('Expected to throw an error')) }) describe('launching', () => { @@ -104,12 +104,7 @@ describe('debug adapter', function () { assert.equal(response.body.breakpoints.length, expectedBreakpoints.length) for(const expected of expectedBreakpoints) { - assert(response.body.breakpoints.some(location => - expected.line === location.line && - expected.column === location.column && - expected.lineEnd === location.lineEnd && - expected.columnEnd === location.columnEnd - ), `expected breakpoint at: ${JSON.stringify(expected)}`) + assert.deepInclude(response.body.breakpoints, expected) } }) }) diff --git a/yarn.lock b/yarn.lock index 1fe8bde..daf3071 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1309,6 +1309,13 @@ __metadata: languageName: node linkType: hard +"@types/chai@npm:^4": + version: 4.3.20 + resolution: "@types/chai@npm:4.3.20" + checksum: 10c0/4601189d611752e65018f1ecadac82e94eed29f348e1d5430e5681a60b01e1ecf855d9bcc74ae43b07394751f184f6970fac2b5561fc57a1f36e93a0f5ffb6e8 + languageName: node + linkType: hard + "@types/expect@npm:^24.3.0": version: 24.3.0 resolution: "@types/expect@npm:24.3.0" @@ -2045,6 +2052,13 @@ __metadata: languageName: node linkType: hard +"assertion-error@npm:^1.1.0": + version: 1.1.0 + resolution: "assertion-error@npm:1.1.0" + checksum: 10c0/25456b2aa333250f01143968e02e4884a34588a8538fbbf65c91a637f1dbfb8069249133cd2f4e530f10f624d206a664e7df30207830b659e9f5298b00a4099b + languageName: node + linkType: hard + "async@npm:^3.2.3": version: 3.2.6 resolution: "async@npm:3.2.6" @@ -2426,6 +2440,21 @@ __metadata: languageName: node linkType: hard +"chai@npm:4.3.10": + version: 4.3.10 + resolution: "chai@npm:4.3.10" + dependencies: + assertion-error: "npm:^1.1.0" + check-error: "npm:^1.0.3" + deep-eql: "npm:^4.1.3" + get-func-name: "npm:^2.0.2" + loupe: "npm:^2.3.6" + pathval: "npm:^1.1.1" + type-detect: "npm:^4.0.8" + checksum: 10c0/c887d24f67be6fb554c7ebbde3bb0568697a8833d475e4768296916891ba143f25fc079f6eb34146f3dd5a3279d34c1f387c32c9a6ab288e579f948d9ccf53fe + languageName: node + linkType: hard + "chalk@npm:5.3.0, chalk@npm:^5.0.0, chalk@npm:^5.3.0": version: 5.3.0 resolution: "chalk@npm:5.3.0" @@ -2474,6 +2503,15 @@ __metadata: languageName: node linkType: hard +"check-error@npm:^1.0.3": + version: 1.0.3 + resolution: "check-error@npm:1.0.3" + dependencies: + get-func-name: "npm:^2.0.2" + checksum: 10c0/94aa37a7315c0e8a83d0112b5bfb5a8624f7f0f81057c73e4707729cdd8077166c6aefb3d8e2b92c63ee130d4a2ff94bad46d547e12f3238cc1d78342a973841 + languageName: node + linkType: hard + "cheerio-select@npm:^2.1.0": version: 2.1.0 resolution: "cheerio-select@npm:2.1.0" @@ -2973,6 +3011,15 @@ __metadata: languageName: node linkType: hard +"deep-eql@npm:^4.1.3": + version: 4.1.4 + resolution: "deep-eql@npm:4.1.4" + dependencies: + type-detect: "npm:^4.0.0" + checksum: 10c0/264e0613493b43552fc908f4ff87b8b445c0e6e075656649600e1b8a17a57ee03e960156fce7177646e4d2ddaf8e5ee616d76bd79929ff593e5c79e4e5e6c517 + languageName: node + linkType: hard + "deep-extend@npm:^0.6.0": version: 0.6.0 resolution: "deep-extend@npm:0.6.0" @@ -4034,6 +4081,13 @@ __metadata: languageName: node linkType: hard +"get-func-name@npm:^2.0.1, get-func-name@npm:^2.0.2": + version: 2.0.2 + resolution: "get-func-name@npm:2.0.2" + checksum: 10c0/89830fd07623fa73429a711b9daecdb304386d237c71268007f788f113505ef1d4cc2d0b9680e072c5082490aec9df5d7758bf5ac6f1c37062855e8e3dc0b9df + languageName: node + linkType: hard + "get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.3, get-intrinsic@npm:^1.2.4": version: 1.2.4 resolution: "get-intrinsic@npm:1.2.4" @@ -5878,6 +5932,15 @@ __metadata: languageName: node linkType: hard +"loupe@npm:^2.3.6": + version: 2.3.7 + resolution: "loupe@npm:2.3.7" + dependencies: + get-func-name: "npm:^2.0.1" + checksum: 10c0/71a781c8fc21527b99ed1062043f1f2bb30bdaf54fa4cf92463427e1718bc6567af2988300bc243c1f276e4f0876f29e3cbf7b58106fdc186915687456ce5bf4 + languageName: node + linkType: hard + "lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": version: 10.4.3 resolution: "lru-cache@npm:10.4.3" @@ -6958,6 +7021,13 @@ __metadata: languageName: node linkType: hard +"pathval@npm:^1.1.1": + version: 1.1.1 + resolution: "pathval@npm:1.1.1" + checksum: 10c0/f63e1bc1b33593cdf094ed6ff5c49c1c0dc5dc20a646ca9725cc7fe7cd9995002d51d5685b9b2ec6814342935748b711bafa840f84c0bb04e38ff40a335c94dc + languageName: node + linkType: hard + "pause-stream@npm:0.0.11": version: 0.0.11 resolution: "pause-stream@npm:0.0.11" @@ -8398,7 +8468,7 @@ __metadata: languageName: node linkType: hard -"type-detect@npm:^4.1.0": +"type-detect@npm:^4.0.0, type-detect@npm:^4.0.8, type-detect@npm:^4.1.0": version: 4.1.0 resolution: "type-detect@npm:4.1.0" checksum: 10c0/df8157ca3f5d311edc22885abc134e18ff8ffbc93d6a9848af5b682730ca6a5a44499259750197250479c5331a8a75b5537529df5ec410622041650a7f293e2a @@ -8858,12 +8928,14 @@ __metadata: version: 0.0.0-use.local resolution: "wollok-debug-adapter@workspace:packages/debug-adapter" dependencies: + "@types/chai": "npm:^4" "@types/mocha": "npm:^10" "@types/node": "npm:^18.14.1" "@types/vscode": "npm:^1.92.0" "@vscode/debugadapter": "npm:^1.66.0" "@vscode/debugadapter-testsupport": "npm:^1.67.0" "@vscode/debugprotocol": "npm:^1.66.0" + chai: "npm:4.3.10" mocha: "npm:^10.7.3" nyc: "npm:^17.0.0" ts-mocha: "npm:^10.0.0"