Skip to content

Commit

Permalink
Add a terminal provider to detect the Java stacktrace printed in term…
Browse files Browse the repository at this point in the history
…inal
  • Loading branch information
testforstephen committed Oct 10, 2020
1 parent 0755aa2 commit f3239ed
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 5 deletions.
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"debugger"
],
"engines": {
"vscode": "^1.47.3"
"vscode": "^1.49.0"
},
"license": "SEE LICENSE IN LICENSE.txt",
"repository": {
Expand Down Expand Up @@ -745,7 +745,7 @@
"@types/lodash": "^4.14.137",
"@types/mocha": "^5.2.7",
"@types/node": "^8.10.51",
"@types/vscode": "1.47.0",
"@types/vscode": "1.49.0",
"cross-env": "^5.2.0",
"gulp": "^4.0.2",
"gulp-tslint": "^8.1.4",
Expand Down
2 changes: 2 additions & 0 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export const JAVA_FETCH_PLATFORM_SETTINGS = "vscode.java.fetchPlatformSettings";

export const JAVA_RESOLVE_CLASSFILTERS = "vscode.java.resolveClassFilters";

export const JAVA_RESOLVE_SOURCE_URI = "vscode.java.resolveSourceUri";

export function executeJavaLanguageServerCommand(...rest) {
return executeJavaExtensionCommand(JAVA_EXECUTE_WORKSPACE_COMMAND, ...rest);
}
Expand Down
2 changes: 2 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { IMainClassOption, IMainMethod, resolveMainClass, resolveMainMethod } fr
import { logger, Type } from "./logger";
import { mainClassPicker } from "./mainClassPicker";
import { pickJavaProcess } from "./processPicker";
import { JavaTerminalLinkProvder } from "./terminalLinkProvider";
import { initializeThreadOperations } from "./threadOperations";
import * as utility from "./utility";

Expand All @@ -34,6 +35,7 @@ function initializeExtension(operationId: string, context: vscode.ExtensionConte

registerDebugEventListener(context);
context.subscriptions.push(logger);
context.subscriptions.push(vscode.window.registerTerminalLinkProvider(new JavaTerminalLinkProvder()));
context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider("java", new JavaDebugConfigurationProvider()));
context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory("java", new JavaDebugAdapterDescriptorFactory()));
context.subscriptions.push(instrumentOperationAsVsCodeCommand("JavaDebug.SpecifyProgramArgs", async () => {
Expand Down
4 changes: 4 additions & 0 deletions src/languageServerPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,7 @@ export function fetchPlatformSettings(): any {
export async function resolveClassFilters(patterns: string[]): Promise<string[]> {
return <string[]> await commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_CLASSFILTERS, ...patterns);
}

export async function resolveSourceUri(line: string): Promise<string> {
return <string> await commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_SOURCE_URI, line);
}
61 changes: 61 additions & 0 deletions src/terminalLinkProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

import { CancellationToken, commands, Position, ProviderResult, Range, TerminalLink, TerminalLinkContext,
TerminalLinkProvider, Uri, window } from "vscode";
import { resolveSourceUri } from "./languageServerPlugin";

export class JavaTerminalLinkProvder implements TerminalLinkProvider<IJavaTerminalLink> {
/**
* Provide terminal links for the given context. Note that this can be called multiple times
* even before previous calls resolve, make sure to not share global objects (eg. `RegExp`)
* that could have problems when asynchronous usage may overlap.
* @param context Information about what links are being provided for.
* @param token A cancellation token.
* @return A list of terminal links for the given line.
*/
public provideTerminalLinks(context: TerminalLinkContext, token: CancellationToken): ProviderResult<IJavaTerminalLink[]> {
if (context.terminal.name !== "Java Debug Console" && context.terminal.name !== "Java Process Console") {
return [];
}

const regex = new RegExp("(\\sat\\s+)([\\w$\\.]+\\/)?(([\\w$]+\\.)+[<\\w$>]+)\\(([\\w-$]+\\.java:\\d+)\\)");
const result: RegExpExecArray = regex.exec(context.line);
if (result && result.length) {
const stackTrace = `${result[2] || ""}${result[3]}(${result[5]})`;
const sourceLineNumber = Number(result[5].split(":")[1]);
return [{
startIndex: result.index + result[1].length,
length: stackTrace.length,
methodName: result[3],
stackTrace,
lineNumber: sourceLineNumber,
}];
}
}

/**
* Handle an activated terminal link.
*/
public async handleTerminalLink(link: IJavaTerminalLink): Promise<void> {
const uri = await resolveSourceUri(link.stackTrace);
if (uri) {
const lineNumber = Math.max(link.lineNumber - 1, 0);
window.showTextDocument(Uri.parse(uri), {
preserveFocus: true,
selection: new Range(new Position(lineNumber, 0), new Position(lineNumber, 0)),
});
} else {
// If no source is found, then open the searching symbols quickpick box.
const fullyQualifiedName = link.methodName.substring(0, link.methodName.lastIndexOf("."));
const className = fullyQualifiedName.substring(fullyQualifiedName.lastIndexOf(".") + 1);
commands.executeCommand("workbench.action.quickOpen", "#" + className);
}
}
}

interface IJavaTerminalLink extends TerminalLink {
methodName: string;
stackTrace: string;
lineNumber: number;
}

0 comments on commit f3239ed

Please sign in to comment.