Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export const JAVA_INFER_LAUNCH_COMMAND_LENGTH = "vscode.java.inferLaunchCommandL

export const JAVA_CHECK_PROJECT_SETTINGS = "vscode.java.checkProjectSettings";

export const JAVA_RESOLVE_ELEMENT_AT_SELECTION = "vscode.java.resolveElementAtSelection";

export function executeJavaLanguageServerCommand(...rest) {
// TODO: need to handle error and trace telemetry
if (!utility.isJavaExtEnabled()) {
Expand Down
14 changes: 14 additions & 0 deletions src/debugCodeLensProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as vscode from "vscode";
import { instrumentOperationAsVsCodeCommand } from "vscode-extension-telemetry-wrapper";

import { JAVA_LANGID } from "./constants";
import { initializeHoverProvider } from "./hoverProvider";
import { IMainMethod, resolveMainMethod } from "./languageServerPlugin";
import { getJavaExtensionAPI, isJavaExtEnabled } from "./utility";

Expand Down Expand Up @@ -37,6 +38,7 @@ class DebugCodeLensContainer implements vscode.Disposable {
private runCommand: vscode.Disposable;
private debugCommand: vscode.Disposable;
private lensProvider: vscode.Disposable | undefined;
private hoverProvider: vscode.Disposable | undefined;
private configurationEvent: vscode.Disposable;

constructor() {
Expand All @@ -48,6 +50,8 @@ class DebugCodeLensContainer implements vscode.Disposable {

if (isCodeLensEnabled) {
this.lensProvider = vscode.languages.registerCodeLensProvider(JAVA_LANGID, new DebugCodeLensProvider());
} else {
this.hoverProvider = initializeHoverProvider();
}

this.configurationEvent = vscode.workspace.onDidChangeConfiguration((event: vscode.ConfigurationChangeEvent) => {
Expand All @@ -60,6 +64,13 @@ class DebugCodeLensContainer implements vscode.Disposable {
this.lensProvider.dispose();
this.lensProvider = undefined;
}

if (newEnabled && this.hoverProvider) {
this.hoverProvider.dispose();
this.hoverProvider = undefined;
} else if (!newEnabled && !this.hoverProvider) {
this.hoverProvider = initializeHoverProvider();
}
}
}, this);
}
Expand All @@ -68,6 +79,9 @@ class DebugCodeLensContainer implements vscode.Disposable {
if (this.lensProvider !== undefined) {
this.lensProvider.dispose();
}
if (this.hoverProvider) {
this.hoverProvider.dispose();
}
this.runCommand.dispose();
this.debugCommand.dispose();
this.configurationEvent.dispose();
Expand Down
84 changes: 84 additions & 0 deletions src/hoverProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

import { CancellationToken, Command, Disposable, Hover, HoverProvider, languages, MarkdownString, Position, ProviderResult, TextDocument,
Uri, window } from "vscode";
import { instrumentOperationAsVsCodeCommand } from "vscode-extension-telemetry-wrapper";
import { JAVA_LANGID } from "./constants";
import { startDebugging } from "./debugCodeLensProvider";
import { resolveElementAtSelection } from "./languageServerPlugin";

const JAVA_HOVER_RUN_COMMAND = "java.debug.runHover";
const MAIN_METHOD_REGEX = /^(public|static|final|synchronized|\s+){4,}void\s+main\s*\(\s*String\s*\[\s*\]\s*\w+\s*\)\s*($|\{)/;

export function initializeHoverProvider(): Disposable {
return new DebugHoverProvider();
}

class DebugHoverProvider implements Disposable {
private runHoverCommand: Disposable;
private hoverProvider: Disposable | undefined;

constructor() {
this.runHoverCommand = instrumentOperationAsVsCodeCommand(JAVA_HOVER_RUN_COMMAND, async (noDebug: boolean, uri: string, position: any) => {
const element = await resolveElementAtSelection(uri, position.line, position.character);
if (element && element.hasMainMethod) {
startDebugging(element.declaringType, element.projectName, Uri.parse(uri), noDebug);
} else {
window.showErrorMessage("The hovered element is not a main method.");
}
});
this.hoverProvider = languages.registerHoverProvider(JAVA_LANGID, new InternalDebugHoverProvider());
}

public dispose() {
if (this.runHoverCommand) {
this.runHoverCommand.dispose();
}

if (this.hoverProvider) {
this.hoverProvider.dispose();
}
}
}

class InternalDebugHoverProvider implements HoverProvider {
public provideHover(document: TextDocument, position: Position, token: CancellationToken): ProviderResult<Hover> {
const range = document.getWordRangeAtPosition(position, /\w+/);
if (!range || document.getText(range) !== "main") {
return;
}

const line = document.lineAt(position);
if (MAIN_METHOD_REGEX.test(line.text.trim()) && this.isMainMethod(line.text.trim())) {
const commands: Command[] = [
{
title: "Run",
command: JAVA_HOVER_RUN_COMMAND,
tooltip: "Run Java Program",
arguments: [ true, document.uri.toString(), { line: position.line, character: position.character }],
},
{
title: "Debug",
command: JAVA_HOVER_RUN_COMMAND,
tooltip: "Debug Java Program",
arguments: [ false, document.uri.toString(), { line: position.line, character: position.character }],
},
];
const contributed = new MarkdownString(commands.map((command) => this.convertCommandToMarkdown(command)).join(" | "));
contributed.isTrusted = true;
return new Hover(contributed);
}
}

private isMainMethod(line: string): boolean {
const modifier: string = line.substring(0, line.indexOf("main"));
const modifiers: string[] = modifier.split(/\s+/);
return modifiers.indexOf("public") >= 0 && modifiers.indexOf("static") >= 0;
}

private convertCommandToMarkdown(command: Command): string {
return `[${command.title}](command:${command.command}?` +
`${encodeURIComponent(JSON.stringify(command.arguments || []))} "${command.tooltip || command.command}")`;
}
}
6 changes: 4 additions & 2 deletions src/languageServerPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
import * as vscode from "vscode";

import * as commands from "./commands";
import { logger, Type } from "./logger";
import * as utility from "./utility";

export interface IMainClassOption {
readonly mainClass: string;
Expand Down Expand Up @@ -75,3 +73,7 @@ export async function detectPreviewFlag(className: string, projectName: string):
};
return checkProjectSettings(className, projectName, true, expectedOptions);
}

export function resolveElementAtSelection(uri: string, line: number, character: number): Promise<any> {
return <Promise<any>>commands.executeJavaLanguageServerCommand(commands.JAVA_RESOLVE_ELEMENT_AT_SELECTION, uri, line, character);
}