ruby support
This commit is contained in:
@@ -25,6 +25,7 @@ See [demos](../demos/) for demos. These languages and debuggers are verified to
|
||||
- C++ using `cppdbg` debug adapter: [✅ Basic Support](../demos/cpp)
|
||||
- Swift using `lldb` debug adapter: [✅ Basic Support](../demos/swift)
|
||||
- Rust using `lldb` debug adapter: [✅ Basic Support](../demos/rust)
|
||||
- Ruby using `rdbg` debug adapter: [✅ Basic Support]
|
||||
|
||||
All other languages and debuggers might work too.
|
||||
For languages with _Basic Support_, only JSON strings can be visualized - you must implement some logic that builds this JSON for your data structure!
|
||||
|
||||
142
extension/src/VisualizationBackend/RbVisualizationSupport.ts
Normal file
142
extension/src/VisualizationBackend/RbVisualizationSupport.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
import { DataExtractionResult, DataResult } from "@hediet/debug-visualizer-data-extraction";
|
||||
import { hotClass, registerUpdateReconciler } from "@hediet/node-reload";
|
||||
import { Config } from "../Config";
|
||||
import { DebuggerViewProxy } from "../proxies/DebuggerViewProxy";
|
||||
import { DebugSessionProxy } from "../proxies/DebugSessionProxy";
|
||||
import { FormattedMessage } from "../webviewContract";
|
||||
import { DebugSessionVisualizationSupport, GetVisualizationDataArgs, VisualizationBackend, VisualizationBackendBase } from "./VisualizationBackend";
|
||||
|
||||
registerUpdateReconciler(module);
|
||||
|
||||
@hotClass(module)
|
||||
export class RbEvaluationEngine implements DebugSessionVisualizationSupport {
|
||||
constructor(
|
||||
private readonly debuggerView: DebuggerViewProxy,
|
||||
private readonly config: Config
|
||||
) { }
|
||||
|
||||
createBackend(
|
||||
session: DebugSessionProxy
|
||||
): VisualizationBackend | undefined {
|
||||
const supportedDebugAdapters = ['rdbg'];
|
||||
|
||||
if (supportedDebugAdapters.indexOf(session.session.type) !== -1) {
|
||||
return new RbVisualizationBackend(
|
||||
session,
|
||||
this.debuggerView,
|
||||
this.config
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
class RbVisualizationBackend extends VisualizationBackendBase {
|
||||
public readonly expressionLanguageId = 'ruby';
|
||||
constructor(
|
||||
debugSession: DebugSessionProxy,
|
||||
debuggerView: DebuggerViewProxy,
|
||||
private readonly config: Config
|
||||
) {
|
||||
super(debugSession, debuggerView)
|
||||
}
|
||||
|
||||
private readonly defaultContext = "repl";
|
||||
|
||||
public async getVisualizationData(
|
||||
args: GetVisualizationDataArgs
|
||||
): Promise<
|
||||
| { kind: "data"; result: DataExtractionResult; }
|
||||
| { kind: "error"; message: FormattedMessage; }
|
||||
> {
|
||||
const result = await this._getVisualizationData(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
private async _getVisualizationData({
|
||||
expression,
|
||||
preferredExtractorId
|
||||
}: GetVisualizationDataArgs): Promise<
|
||||
| { kind: "data"; result: DataExtractionResult }
|
||||
| { kind: "error"; message: FormattedMessage }
|
||||
> {
|
||||
try {
|
||||
if (expression.length === 0) throw new Error("No extractors");
|
||||
|
||||
const frameId = this.debuggerView.getActiveStackFrameId(
|
||||
this.debugSession
|
||||
);
|
||||
const initialReply = await this.debugSession.evaluate({
|
||||
expression: "require 'debugvisualizer'",
|
||||
frameId,
|
||||
context: this.defaultContext
|
||||
});
|
||||
if (initialReply.result.includes('LoadError')) {
|
||||
return {
|
||||
kind: "error",
|
||||
message: {
|
||||
kind: "list",
|
||||
items: [
|
||||
"LoadError: Failed to load debugvisualizer.",
|
||||
{
|
||||
kind: "inlineList",
|
||||
items: [
|
||||
"Install the gem by executing:",
|
||||
{ kind: "code", content: "$ bundle add debugvisualizer" },
|
||||
],
|
||||
},
|
||||
{
|
||||
kind: "inlineList",
|
||||
items: [
|
||||
"If bundler is not being used, install the gem by executing:",
|
||||
{ kind: "code", content: "$ gem install debugvisualizer" },
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
||||
};
|
||||
}
|
||||
const preferredId = preferredExtractorId || '';
|
||||
const wrappedExpr = `
|
||||
DebugVisualizer.to_debug_visualizer_protocol_json("${preferredId}", ${expression})
|
||||
`;
|
||||
const reply = await this.debugSession.evaluate({
|
||||
expression: wrappedExpr,
|
||||
frameId,
|
||||
context: this.defaultContext
|
||||
})
|
||||
|
||||
let dataResult: DataResult;
|
||||
try {
|
||||
// Debuggee converts result to a JSON string twice.
|
||||
dataResult = JSON.parse(JSON.parse(reply.result)) as DataResult;
|
||||
} catch (error) {
|
||||
let message = error.message
|
||||
// The `reply.result` is as follows when error occurs in the evaluation of an expression and parsing will fail.
|
||||
// e.g. "#<ZeroDivisionError: divided by 0>"
|
||||
// In this case, it is more beneficial to display this error message.
|
||||
if (reply.result.includes('Error')) message = reply.result;
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
switch (dataResult.kind) {
|
||||
case 'NoExtractors':
|
||||
throw new Error("No extractors");
|
||||
case 'Error':
|
||||
throw new Error(dataResult.message);
|
||||
case 'Data':
|
||||
return {
|
||||
kind: "data",
|
||||
result: dataResult.extractionResult,
|
||||
};
|
||||
default:
|
||||
throw new Error("Invalid Data");
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
kind: "error",
|
||||
message: error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,5 +3,6 @@ export * from "./VisualizationBackend";
|
||||
export { ComposedVisualizationSupport } from "./ComposedVisualizationSupport";
|
||||
export { JsEvaluationEngine } from "./JsVisualizationSupport";
|
||||
export { PyEvaluationEngine } from "./PyVisualizationSupport";
|
||||
export { RbEvaluationEngine } from './RbVisualizationSupport';
|
||||
export { GenericVisualizationSupport } from "./GenericVisualizationSupport";
|
||||
export { ConfigurableVisualizationSupport } from "./ConfigurableVisualizationSupport";
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
ComposedVisualizationSupport,
|
||||
JsEvaluationEngine,
|
||||
PyEvaluationEngine,
|
||||
RbEvaluationEngine,
|
||||
GenericVisualizationSupport,
|
||||
ConfigurableVisualizationSupport,
|
||||
} from "./VisualizationBackend";
|
||||
@@ -53,6 +54,7 @@ export class Extension {
|
||||
),
|
||||
new JsEvaluationEngine(this.debuggerView, this.config),
|
||||
new PyEvaluationEngine(this.debuggerView, this.config),
|
||||
new RbEvaluationEngine(this.debuggerView, this.config),
|
||||
new GenericVisualizationSupport(this.debuggerView),
|
||||
]),
|
||||
this.debuggerView
|
||||
@@ -102,7 +104,7 @@ export class Extension {
|
||||
if (
|
||||
match.index <= selection.start.character &&
|
||||
selection.start.character <=
|
||||
match.index + match[0].length
|
||||
match.index + match[0].length
|
||||
) {
|
||||
selectedText = match[1];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user