Skip to content

Commit 5668efb

Browse files
authored
Merge pull request #159 from ono-max/patch-1
ruby support
2 parents 307adad + 7551b76 commit 5668efb

File tree

4 files changed

+147
-1
lines changed

4 files changed

+147
-1
lines changed

extension/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ See [demos](../demos/) for demos. These languages and debuggers are verified to
2525
- C++ using `cppdbg` debug adapter: [✅ Basic Support](../demos/cpp)
2626
- Swift using `lldb` debug adapter: [✅ Basic Support](../demos/swift)
2727
- Rust using `lldb` debug adapter: [✅ Basic Support](../demos/rust)
28+
- Ruby using `rdbg` debug adapter: [✅ Basic Support]
2829

2930
All other languages and debuggers might work too.
3031
For languages with _Basic Support_, only JSON strings can be visualized - you must implement some logic that builds this JSON for your data structure!
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import { DataExtractionResult, DataResult } from "@hediet/debug-visualizer-data-extraction";
2+
import { hotClass, registerUpdateReconciler } from "@hediet/node-reload";
3+
import { Config } from "../Config";
4+
import { DebuggerViewProxy } from "../proxies/DebuggerViewProxy";
5+
import { DebugSessionProxy } from "../proxies/DebugSessionProxy";
6+
import { FormattedMessage } from "../webviewContract";
7+
import { DebugSessionVisualizationSupport, GetVisualizationDataArgs, VisualizationBackend, VisualizationBackendBase } from "./VisualizationBackend";
8+
9+
registerUpdateReconciler(module);
10+
11+
@hotClass(module)
12+
export class RbEvaluationEngine implements DebugSessionVisualizationSupport {
13+
constructor(
14+
private readonly debuggerView: DebuggerViewProxy,
15+
private readonly config: Config
16+
) { }
17+
18+
createBackend(
19+
session: DebugSessionProxy
20+
): VisualizationBackend | undefined {
21+
const supportedDebugAdapters = ['rdbg'];
22+
23+
if (supportedDebugAdapters.indexOf(session.session.type) !== -1) {
24+
return new RbVisualizationBackend(
25+
session,
26+
this.debuggerView,
27+
this.config
28+
);
29+
}
30+
return undefined;
31+
}
32+
}
33+
34+
class RbVisualizationBackend extends VisualizationBackendBase {
35+
public readonly expressionLanguageId = 'ruby';
36+
constructor(
37+
debugSession: DebugSessionProxy,
38+
debuggerView: DebuggerViewProxy,
39+
private readonly config: Config
40+
) {
41+
super(debugSession, debuggerView)
42+
}
43+
44+
private readonly defaultContext = "repl";
45+
46+
public async getVisualizationData(
47+
args: GetVisualizationDataArgs
48+
): Promise<
49+
| { kind: "data"; result: DataExtractionResult; }
50+
| { kind: "error"; message: FormattedMessage; }
51+
> {
52+
const result = await this._getVisualizationData(args);
53+
return result;
54+
}
55+
56+
private async _getVisualizationData({
57+
expression,
58+
preferredExtractorId
59+
}: GetVisualizationDataArgs): Promise<
60+
| { kind: "data"; result: DataExtractionResult }
61+
| { kind: "error"; message: FormattedMessage }
62+
> {
63+
try {
64+
if (expression.length === 0) throw new Error("No extractors");
65+
66+
const frameId = this.debuggerView.getActiveStackFrameId(
67+
this.debugSession
68+
);
69+
const initialReply = await this.debugSession.evaluate({
70+
expression: "require 'debugvisualizer'",
71+
frameId,
72+
context: this.defaultContext
73+
});
74+
if (initialReply.result.includes('LoadError')) {
75+
return {
76+
kind: "error",
77+
message: {
78+
kind: "list",
79+
items: [
80+
"LoadError: Failed to load debugvisualizer.",
81+
{
82+
kind: "inlineList",
83+
items: [
84+
"Install the gem by executing:",
85+
{ kind: "code", content: "$ bundle add debugvisualizer" },
86+
],
87+
},
88+
{
89+
kind: "inlineList",
90+
items: [
91+
"If bundler is not being used, install the gem by executing:",
92+
{ kind: "code", content: "$ gem install debugvisualizer" },
93+
],
94+
}
95+
],
96+
}
97+
};
98+
}
99+
const preferredId = preferredExtractorId || '';
100+
const wrappedExpr = `
101+
DebugVisualizer.to_debug_visualizer_protocol_json("${preferredId}", ${expression})
102+
`;
103+
const reply = await this.debugSession.evaluate({
104+
expression: wrappedExpr,
105+
frameId,
106+
context: this.defaultContext
107+
})
108+
109+
let dataResult: DataResult;
110+
try {
111+
// Debuggee converts result to a JSON string twice.
112+
dataResult = JSON.parse(JSON.parse(reply.result)) as DataResult;
113+
} catch (error) {
114+
let message = error.message
115+
// The `reply.result` is as follows when error occurs in the evaluation of an expression and parsing will fail.
116+
// e.g. "#<ZeroDivisionError: divided by 0>"
117+
// In this case, it is more beneficial to display this error message.
118+
if (reply.result.includes('Error')) message = reply.result;
119+
throw new Error(message)
120+
}
121+
122+
switch (dataResult.kind) {
123+
case 'NoExtractors':
124+
throw new Error("No extractors");
125+
case 'Error':
126+
throw new Error(dataResult.message);
127+
case 'Data':
128+
return {
129+
kind: "data",
130+
result: dataResult.extractionResult,
131+
};
132+
default:
133+
throw new Error("Invalid Data");
134+
}
135+
} catch (error) {
136+
return {
137+
kind: "error",
138+
message: error.message
139+
};
140+
}
141+
}
142+
}

extension/src/VisualizationBackend/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ export * from "./VisualizationBackend";
33
export { ComposedVisualizationSupport } from "./ComposedVisualizationSupport";
44
export { JsEvaluationEngine } from "./JsVisualizationSupport";
55
export { PyEvaluationEngine } from "./PyVisualizationSupport";
6+
export { RbEvaluationEngine } from './RbVisualizationSupport';
67
export { GenericVisualizationSupport } from "./GenericVisualizationSupport";
78
export { ConfigurableVisualizationSupport } from "./ConfigurableVisualizationSupport";

extension/src/extension.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
ComposedVisualizationSupport,
2323
JsEvaluationEngine,
2424
PyEvaluationEngine,
25+
RbEvaluationEngine,
2526
GenericVisualizationSupport,
2627
ConfigurableVisualizationSupport,
2728
} from "./VisualizationBackend";
@@ -53,6 +54,7 @@ export class Extension {
5354
),
5455
new JsEvaluationEngine(this.debuggerView, this.config),
5556
new PyEvaluationEngine(this.debuggerView, this.config),
57+
new RbEvaluationEngine(this.debuggerView, this.config),
5658
new GenericVisualizationSupport(this.debuggerView),
5759
]),
5860
this.debuggerView
@@ -102,7 +104,7 @@ export class Extension {
102104
if (
103105
match.index <= selection.start.character &&
104106
selection.start.character <=
105-
match.index + match[0].length
107+
match.index + match[0].length
106108
) {
107109
selectedText = match[1];
108110
}

0 commit comments

Comments
 (0)