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
12 changes: 12 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@
],
"main": "./dist/extension",
"contributes": {
"customEditors": [
{
"viewType": "decompiled.javaClass",
"displayName": "Decompiled Java Class File",
"selector": [
{
"scheme": "file",
"filenamePattern": "*.class"
}
]
}
],
"javaBuildFilePatterns": [
"^pom\\.xml$",
".*\\.gradle(\\.kts)?$"
Expand Down
40 changes: 23 additions & 17 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ export namespace Commands {
*/
export const EXECUTE_WORKSPACE_COMMAND = 'java.execute.workspaceCommand';

/**
* Execute Workspace build (compilation)
*/
/**
* Execute Workspace build (compilation)
*/
export const COMPILE_WORKSPACE = 'java.workspace.compile';

/**
Expand Down Expand Up @@ -119,18 +119,18 @@ export namespace Commands {
*/
export const OPEN_FORMATTER = 'java.open.formatter.settings';

/**
* Open a file given the URI
*/
export const OPEN_FILE = 'java.open.file';
/**
* Open a file given the URI
*/
export const OPEN_FILE = 'java.open.file';

/**
* Clean the Java language server workspace
*/
export const CLEAN_WORKSPACE = 'java.clean.workspace';
/**
* Update the source attachment for the selected class file
* client-side & server-side commands
* client-side & server-side commands
*/
export const UPDATE_SOURCE_ATTACHMENT_CMD = 'java.project.updateSourceAttachment.command';
export const UPDATE_SOURCE_ATTACHMENT = 'java.project.updateSourceAttachment';
Expand All @@ -140,25 +140,25 @@ export namespace Commands {
export const RESOLVE_SOURCE_ATTACHMENT = 'java.project.resolveSourceAttachment';
/**
* Mark the folder as the source root of the closest project.
* client-side & server-side commands
* client-side & server-side commands
*/
export const ADD_TO_SOURCEPATH_CMD = 'java.project.addToSourcePath.command';
export const ADD_TO_SOURCEPATH = 'java.project.addToSourcePath';
/**
* Unmark the folder as the source root of the project.
* client-side & server-side commands
* client-side & server-side commands
*/
export const REMOVE_FROM_SOURCEPATH_CMD = 'java.project.removeFromSourcePath.command';
export const REMOVE_FROM_SOURCEPATH = 'java.project.removeFromSourcePath';
/**
* List all recognized source roots in the workspace.
* client-side & server-side commands
* client-side & server-side commands
*/
export const LIST_SOURCEPATHS_CMD = 'java.project.listSourcePaths.command';
export const LIST_SOURCEPATHS = 'java.project.listSourcePaths';
/**
* Import new projects
* client-side & server-side commands
* client-side & server-side commands
*/
export const IMPORT_PROJECTS_CMD = 'java.project.import.command';
export const IMPORT_PROJECTS = 'java.project.import';
Expand All @@ -170,7 +170,7 @@ export namespace Commands {
* Generate hashCode() and equals().
*/
export const HASHCODE_EQUALS_PROMPT = 'java.action.hashCodeEqualsPrompt';
/**
/**
* Open settings.json
*/
export const OPEN_JSON_SETTINGS = 'workbench.action.openSettingsJson';
Expand All @@ -182,10 +182,10 @@ export namespace Commands {
* Organize imports silently.
*/
export const ORGANIZE_IMPORTS_SILENTLY = "java.edit.organizeImports";
/**
* Handle a paste event.
*/
export const HANDLE_PASTE_EVENT = "java.edit.handlePasteEvent";
/**
* Handle a paste event.
*/
export const HANDLE_PASTE_EVENT = "java.edit.handlePasteEvent";
/**
* Custom paste action (triggers auto-import)
*/
Expand Down Expand Up @@ -320,4 +320,10 @@ export namespace Commands {
* Clean everything in the shared index directory.
*/
export const CLEAN_SHARED_INDEXES = "java.clean.sharedIndexes";

/**
* Get the uri of the decompiled class file.
*/
export const GET_DECOMPILED_SOURCE = "java.decompile";

}
14 changes: 9 additions & 5 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { runtimeStatusBarProvider } from './runtimeStatusBarProvider';
import { serverStatusBarProvider } from './serverStatusBarProvider';
import { ACTIVE_BUILD_TOOL_STATE, cleanWorkspaceFileName, getJavaServerMode, handleTextBlockClosing, onConfigurationChange, ServerMode } from './settings';
import { snippetCompletionProvider } from './snippetCompletionProvider';
import { JavaClassEditorProvider } from './javaClassEditor';
import { StandardLanguageClient } from './standardLanguageClient';
import { SyntaxLanguageClient } from './syntaxLanguageClient';
import { convertToGlob, deleteDirectory, ensureExists, getBuildFilePatterns, getExclusionBlob, getInclusionPatternsFromNegatedExclusion, getJavaConfig, getJavaConfiguration, hasBuildToolConflicts } from './utils';
Expand Down Expand Up @@ -191,7 +192,7 @@ export function activate(context: ExtensionContext): Promise<ExtensionAPI> {
range: client.code2ProtocolConverter.asRange(range),
context: await client.code2ProtocolConverter.asCodeActionContext(context)
};
const showAt = getJavaConfiguration().get<string>("quickfix.showAt");
const showAt = getJavaConfiguration().get<string>("quickfix.showAt");
if (showAt === 'line' && range.start.line === range.end.line && range.start.character === range.end.character) {
const textLine = document.lineAt(params.range.start.line);
if (textLine !== null) {
Expand Down Expand Up @@ -362,6 +363,9 @@ export function activate(context: ExtensionContext): Promise<ExtensionAPI> {
context.subscriptions.push(serverStatusBarProvider);
context.subscriptions.push(runtimeStatusBarProvider);

const classEditorProviderRegistration = window.registerCustomEditorProvider(JavaClassEditorProvider.viewType, new JavaClassEditorProvider(context));
context.subscriptions.push(classEditorProviderRegistration);

registerClientProviders(context, { contentProviderEvent: jdtEventEmitter.event });

apiManager.getApiInstance().onDidServerModeChange((event: ServerMode) => {
Expand Down Expand Up @@ -448,7 +452,7 @@ async function workspaceContainsBuildFiles(): Promise<boolean> {
const inclusionPatterns: string[] = getBuildFilePatterns();
const inclusionPatternsFromNegatedExclusion: string[] = getInclusionPatternsFromNegatedExclusion();
if (inclusionPatterns.length > 0 && inclusionPatternsFromNegatedExclusion.length > 0 &&
(await workspace.findFiles(convertToGlob(inclusionPatterns, inclusionPatternsFromNegatedExclusion), null, 1 /* maxResults */)).length > 0) {
(await workspace.findFiles(convertToGlob(inclusionPatterns, inclusionPatternsFromNegatedExclusion), null, 1 /* maxResults */)).length > 0) {
return true;
}

Expand Down Expand Up @@ -485,7 +489,7 @@ async function ensureNoBuildToolConflicts(context: ExtensionContext, clientOptio
clientOptions.initializationOptions.settings.java.import.maven.enabled = false;
context.workspaceState.update(ACTIVE_BUILD_TOOL_STATE, "gradle");
} else {
throw new Error (`Unknown build tool: ${activeBuildTool}`); // unreachable
throw new Error(`Unknown build tool: ${activeBuildTool}`); // unreachable
}
}

Expand Down Expand Up @@ -699,7 +703,7 @@ function openLogFile(logFile, openingFailureWarning: string, column: ViewColumn
if (!doc) {
return false;
}
return window.showTextDocument(doc, {viewColumn: column, preview: false})
return window.showTextDocument(doc, { viewColumn: column, preview: false })
.then(editor => !!editor);
}, () => false)
.then(didOpen => {
Expand Down Expand Up @@ -937,7 +941,7 @@ async function cleanJavaWorkspaceStorage() {
}
}
});
}
}
}

function registerOutOfMemoryDetection(storagePath: string) {
Expand Down
67 changes: 67 additions & 0 deletions src/javaClassEditor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import path = require('path');
import * as vscode from 'vscode';
import { Uri, window, ExtensionContext} from "vscode";
import { getNonce } from "./webviewUtils";

class JavaClassDocument implements vscode.CustomDocument {
constructor(uri: Uri) { this.uri = uri; }
uri: Uri;
dispose(): void { }
}

export class JavaClassEditorProvider implements vscode.CustomReadonlyEditorProvider {

private context: ExtensionContext;

openCustomDocument(uri: Uri, openContext: vscode.CustomDocumentOpenContext, token: vscode.CancellationToken): JavaClassDocument {
return new JavaClassDocument(uri);
}

constructor (context: ExtensionContext) {
this.context = context;
}

public static readonly viewType = 'decompiled.javaClass';

async resolveCustomEditor(document: vscode.CustomDocument, webviewPanel: vscode.WebviewPanel, token: vscode.CancellationToken): Promise<void> {
const nonce: string = getNonce();
webviewPanel.webview.options = {
enableScripts: true,
localResourceRoots: [Uri.joinPath(Uri.parse(this.context.extensionPath), 'webview-resources')]
};
const classUri = Uri.parse((document.uri.toString()).replace(/^file/, "class"));
const styleUri = Uri.file(
path.join(this.context.extensionPath, 'webview-resources', 'button.css')
);
const style: string = `<link rel="stylesheet" type="text/css" href="${webviewPanel.webview.asWebviewUri(styleUri).toString()}">`;
webviewPanel.webview.html = `
<html lang="en">
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'nonce-${nonce}'; style-src ${webviewPanel.webview.cspSource};">
${style}
</head>
<body>
<div class="center">
<p>This file is not displayed in the text editor because it is a Java class file. Click here to decompile and open.</p>
<button id="btn"><center>Decompile Class File</center></button>
<div>
<script nonce="${nonce}">
Comment thread
JessicaJHee marked this conversation as resolved.
const vscode = acquireVsCodeApi();
document.getElementById("btn").addEventListener("click", decompiled);
function decompiled() {
vscode.postMessage({ command: 'decompiled' });
}
</script>
</body>
</html>
`;
webviewPanel.webview.onDidReceiveMessage(message => {
switch (message.command) {
case 'decompiled':
webviewPanel.dispose();
window.showTextDocument(classUri, { preview: false });
return;
}
}, undefined, this.context.subscriptions);
}
}
34 changes: 29 additions & 5 deletions src/providerDispatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@ export function registerClientProviders(context: ExtensionContext, options: Prov
const jdtProvider = createJDTContentProvider(options);
context.subscriptions.push(workspace.registerTextDocumentContentProvider('jdt', jdtProvider));

const classProvider = createClassContentProvider(options);
context.subscriptions.push(workspace.registerTextDocumentContentProvider('class', classProvider));

overwriteWorkspaceSymbolProvider(context);

return {
handles: [hoverProvider, symbolProvider, jdtProvider]
handles: [hoverProvider, symbolProvider, jdtProvider, classProvider]
};
}

Expand Down Expand Up @@ -81,6 +84,27 @@ function createJDTContentProvider(options: ProviderOptions): TextDocumentContent
};
}

function createClassContentProvider(options: ProviderOptions): TextDocumentContentProvider {
return <TextDocumentContentProvider>{
onDidChange: options.contentProviderEvent,
provideTextDocumentContent: async (uri: Uri, token: CancellationToken): Promise<string> => {
const languageClient: LanguageClient | undefined = await getActiveLanguageClient();

if (!languageClient) {
return '';
}
const originalUri = uri.toString().replace(/^class/, "file");
const decompiledContent: string = await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.GET_DECOMPILED_SOURCE, originalUri);
if (!decompiledContent) {
console.log(`Error while getting decompiled source : ${originalUri}`);
return "Error while getting decompiled source.";
} else {
return decompiledContent;
}
}
};
}

function createDocumentSymbolProvider(): DocumentSymbolProvider {
return <DocumentSymbolProvider>{
provideDocumentSymbols: async (document: TextDocument, token: CancellationToken): Promise<SymbolInformation[] | DocumentSymbol[]> => {
Expand Down Expand Up @@ -109,7 +133,7 @@ function createDocumentSymbolProvider(): DocumentSymbolProvider {

const START_OF_DOCUMENT = new Range(new Position(0, 0), new Position(0, 0));

function createWorkspaceSymbolProvider(existingWorkspaceSymbolProvider: WorkspaceSymbolProvider): WorkspaceSymbolProvider {
function createWorkspaceSymbolProvider(existingWorkspaceSymbolProvider: WorkspaceSymbolProvider): WorkspaceSymbolProvider {
return {
provideWorkspaceSymbols: async (query: string, token: CancellationToken) => {
// This is a workaround until vscode add support for qualified symbol search which is tracked by
Expand Down Expand Up @@ -159,9 +183,9 @@ function createWorkspaceSymbolProvider(existingWorkspaceSymbolProvider: Workspac
}

function overwriteWorkspaceSymbolProvider(context: ExtensionContext): void {
const disposable = apiManager.getApiInstance().onDidServerModeChange( async (mode) => {
const disposable = apiManager.getApiInstance().onDidServerModeChange(async (mode) => {
if (mode === ServerMode.standard) {
const feature = (await getActiveLanguageClient()).getFeature(WorkspaceSymbolRequest.method);
const feature = (await getActiveLanguageClient()).getFeature(WorkspaceSymbolRequest.method);
const providers = feature.getProviders();
if (providers && providers.length > 0) {
feature.dispose();
Expand All @@ -186,7 +210,7 @@ const REPLACE_JDT_LINKS_PATTERN: RegExp = /(\[(?:[^\]])+\]\()(jdt:\/\/(?:(?:(?:\
* @returns the hover with all jdt:// links replaced with a command:// link that opens the jdt URI
*/
function fixJdtSchemeHoverLinks(hover: Hover): Hover {
const newContents: (MarkedString|MarkdownString)[] = [];
const newContents: (MarkedString | MarkdownString)[] = [];
for (const content of hover.contents) {
if (content instanceof MarkdownString) {
const newContent: string = (<MarkdownString>content).value.replace(REPLACE_JDT_LINKS_PATTERN, (_substring, group1, group2) => {
Expand Down
34 changes: 34 additions & 0 deletions webview-resources/button.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/* https://css-tricks.com/overriding-default-button-styles/ */

button {
display: inline-block;
border: none;
padding: 12px 16px;
margin: 0;
text-decoration: none;
background: var(--vscode-button-background);
color: #ffffff;
font-family: sans-serif;
font-size: 14px;
cursor: pointer;
text-align: center;
}

button:hover,
button:focus {
background: var(--vscode-button-hoverBackground);
}
button:focus {
outline: 1px solid var(--vscode-button-hoverBackground);
/* outline-offset: -4px; */
}

.center {
text-align: center;
margin: 0;
position: absolute;
top: 50%;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}