-
Notifications
You must be signed in to change notification settings - Fork 96
feat(ide): IntelliJ-grade Java in the web IDE — New→Java scaffolding, full JDT.LS IntelliSense, refactoring, Call/Type Hierarchy & performance #6076
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
46d59c8
feat(ide): IntelliJ-style Java authoring — New → Java scaffolding + f…
iliyan-velichkov 880a01b
fix(engine-java-lsp): raise JDT.LS heap (configurable) + make CreateJ…
iliyan-velichkov fb31f74
fix(ide): New→Java reveals the created file in the tree; Repository e…
iliyan-velichkov 744f4b8
feat(editor): cross-file Go to Definition/References + better Java co…
iliyan-velichkov 08cc829
feat(editor): cross-file Java rename — references, and the type's own…
iliyan-velichkov ae1e546
feat(editor): more IntelliJ-style Java — outline, folding, inlay hint…
iliyan-velichkov 1dcc9a3
fix(editor): references code preview, first-Ctrl+Space completion, ke…
iliyan-velichkov 29175bc
fix(editor): make Java quick-fixes & source actions work; refresh tre…
iliyan-velichkov f4c9858
fix(editor): correct & sync Java type rename (stale error, old name i…
iliyan-velichkov acca89e
perf+ux(editor): cap completion results; reveal renamed file in the tree
iliyan-velichkov e84ebf9
feat(editor): surface refactor assists via the lightbulb (assign para…
iliyan-velichkov e768c87
perf(editor): cut Java LSP load — code-action triggerKind, no referen…
iliyan-velichkov 7d4717d
fix(ide): move New→Java to a top-level "Java" menu (unbreak New scrol…
iliyan-velichkov a9a2be0
perf(java-lsp): persist JDT.LS index across restarts; pre-warm classp…
iliyan-velichkov 3610039
feat(editor): Java quick wins — postfix completion, organize-imports …
iliyan-velichkov 70c0a1c
feat(editor): Java semantic highlighting via JDT.LS semantic tokens
iliyan-velichkov 1eaebd5
feat(ide,java-lsp): server REST facade + workspace Go to Symbol view …
iliyan-velichkov 5463bfd
feat(ide): Call & Type Hierarchy panel (Ctrl+Alt+H / Ctrl+H)
iliyan-velichkov 698b2a7
fix(ide): remove Go to Symbol view; fix Call/Type Hierarchy lazy-load…
iliyan-velichkov 57b6c31
fix(java-lsp): hierarchy endpoint 500 — Jackson 2 JsonNode vs Spring …
iliyan-velichkov 777ddd0
ci(codeql): exclude generated java-lsp-client.js bundle from analysis
iliyan-velichkov 09a1d04
refactor(tests): drive cascading context menu with Selenide APIs, not…
iliyan-velichkov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
176 changes: 176 additions & 0 deletions
176
...ain/java/org/eclipse/dirigible/components/ide/lsp/java/endpoint/JavaLspQueryEndpoint.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| /* | ||
| * Copyright (c) 2010-2026 Eclipse Dirigible contributors | ||
| * | ||
| * All rights reserved. This program and the accompanying materials are made available under the | ||
| * terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at | ||
| * http://www.eclipse.org/legal/epl-v20.html | ||
| * | ||
| * SPDX-FileCopyrightText: Eclipse Dirigible contributors SPDX-License-Identifier: EPL-2.0 | ||
| */ | ||
| package org.eclipse.dirigible.components.ide.lsp.java.endpoint; | ||
|
|
||
| import com.fasterxml.jackson.databind.JsonNode; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import com.fasterxml.jackson.databind.node.ObjectNode; | ||
| import jakarta.annotation.security.RolesAllowed; | ||
| import org.eclipse.dirigible.components.base.endpoint.BaseEndpoint; | ||
| import org.eclipse.dirigible.components.ide.lsp.java.process.JdtLsInstance; | ||
| import org.eclipse.dirigible.components.ide.lsp.java.process.JdtLsManager; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.http.MediaType; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.annotation.PostMapping; | ||
| import org.springframework.web.bind.annotation.RequestBody; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
| import org.springframework.web.server.ResponseStatusException; | ||
|
|
||
| import java.security.Principal; | ||
| import java.util.concurrent.TimeUnit; | ||
|
|
||
| /** | ||
| * HTTP facade over a workspace's JDT Language Server for IDE views that live outside the Monaco | ||
| * editor iframe (Call Hierarchy, Type Hierarchy). Those views cannot reuse the editor's per-iframe | ||
| * LSP WebSocket, so each request here drives the shared {@link JdtLsInstance} directly. | ||
| * | ||
| * <p> | ||
| * Request and response bodies are handled as raw JSON strings, parsed/produced with this module's | ||
| * own Jackson 2 {@link ObjectMapper}. Spring Boot 4's default HTTP message converter is Jackson 3 | ||
| * ({@code tools.jackson}), which cannot bind to Jackson 2 {@code JsonNode} types — so the | ||
| * controller boundary deliberately stays on {@code String} and never exposes a {@code JsonNode} | ||
| * parameter or return value. | ||
| * | ||
| * <p> | ||
| * URIs travel as the browser's virtual form ({@code file:///workspace/<ws>/...}); the bridge | ||
| * translates to/from the real on-disk paths, so responses come back virtual and are usable by the | ||
| * views as-is. | ||
| */ | ||
| @RestController | ||
| @RequestMapping(BaseEndpoint.PREFIX_ENDPOINT_IDE + "java-lsp") | ||
| @RolesAllowed({"ADMINISTRATOR", "DEVELOPER"}) | ||
| @ConditionalOnProperty(name = "java.lsp.enabled", havingValue = "true", matchIfMissing = true) | ||
| public class JavaLspQueryEndpoint { | ||
|
|
||
| private static final Logger logger = LoggerFactory.getLogger(JavaLspQueryEndpoint.class); | ||
|
|
||
| private static final long REQUEST_TIMEOUT_SECONDS = 60; | ||
|
|
||
| private final JdtLsManager manager; | ||
| private final ObjectMapper mapper = new ObjectMapper(); | ||
|
|
||
| public JavaLspQueryEndpoint(JdtLsManager manager) { | ||
| this.manager = manager; | ||
| } | ||
|
|
||
| /** Resolves the call-hierarchy item at a position ({@code textDocument/prepareCallHierarchy}). */ | ||
| @PostMapping("/call-hierarchy/prepare") | ||
| public ResponseEntity<String> prepareCallHierarchy(@RequestBody String body, Principal principal) { | ||
| JsonNode b = parse(body); | ||
| return json(request(principal, workspaceOf(b), "textDocument/prepareCallHierarchy", positionParams(b))); | ||
| } | ||
|
|
||
| /** Callers of the given call-hierarchy item ({@code callHierarchy/incomingCalls}). */ | ||
| @PostMapping("/call-hierarchy/incoming") | ||
| public ResponseEntity<String> incomingCalls(@RequestBody String body, Principal principal) { | ||
| JsonNode b = parse(body); | ||
| return json(request(principal, workspaceOf(b), "callHierarchy/incomingCalls", itemParams(b))); | ||
| } | ||
|
|
||
| /** Callees of the given call-hierarchy item ({@code callHierarchy/outgoingCalls}). */ | ||
| @PostMapping("/call-hierarchy/outgoing") | ||
| public ResponseEntity<String> outgoingCalls(@RequestBody String body, Principal principal) { | ||
| JsonNode b = parse(body); | ||
| return json(request(principal, workspaceOf(b), "callHierarchy/outgoingCalls", itemParams(b))); | ||
| } | ||
|
|
||
| /** Resolves the type-hierarchy item at a position ({@code textDocument/prepareTypeHierarchy}). */ | ||
| @PostMapping("/type-hierarchy/prepare") | ||
| public ResponseEntity<String> prepareTypeHierarchy(@RequestBody String body, Principal principal) { | ||
| JsonNode b = parse(body); | ||
| return json(request(principal, workspaceOf(b), "textDocument/prepareTypeHierarchy", positionParams(b))); | ||
| } | ||
|
|
||
| /** Supertypes of the given type-hierarchy item ({@code typeHierarchy/supertypes}). */ | ||
| @PostMapping("/type-hierarchy/supertypes") | ||
| public ResponseEntity<String> supertypes(@RequestBody String body, Principal principal) { | ||
| JsonNode b = parse(body); | ||
| return json(request(principal, workspaceOf(b), "typeHierarchy/supertypes", itemParams(b))); | ||
| } | ||
|
|
||
| /** Subtypes of the given type-hierarchy item ({@code typeHierarchy/subtypes}). */ | ||
| @PostMapping("/type-hierarchy/subtypes") | ||
| public ResponseEntity<String> subtypes(@RequestBody String body, Principal principal) { | ||
| JsonNode b = parse(body); | ||
| return json(request(principal, workspaceOf(b), "typeHierarchy/subtypes", itemParams(b))); | ||
| } | ||
|
|
||
| // ------------------------------------------------------------------------- | ||
|
|
||
| private JsonNode parse(String body) { | ||
| try { | ||
| return mapper.readTree(body); | ||
| } catch (Exception e) { | ||
| throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid JSON body", e); | ||
| } | ||
| } | ||
|
|
||
| private static ResponseEntity<String> json(JsonNode node) { | ||
| return ResponseEntity.ok() | ||
| .contentType(MediaType.APPLICATION_JSON) | ||
| .body(node.toString()); | ||
| } | ||
|
|
||
| private JsonNode request(Principal principal, String workspace, String method, JsonNode params) { | ||
| if (workspace == null || workspace.isBlank()) { | ||
| throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Missing 'workspace'"); | ||
| } | ||
| String username = principal != null ? principal.getName() : "anonymous"; | ||
| try { | ||
| JdtLsInstance instance = manager.getOrStart(username, workspace); | ||
| instance.ensureInitialized() | ||
| .get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); | ||
| JsonNode response = instance.sendRequest(method, mapper.writeValueAsString(params)) | ||
| .get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); | ||
| return response.path("result"); | ||
| } catch (IllegalStateException notReady) { | ||
| // JDT.LS still starting up or not bundled in this build. | ||
| throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE, notReady.getMessage(), notReady); | ||
| } catch (InterruptedException ie) { | ||
| Thread.currentThread() | ||
| .interrupt(); | ||
| throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE, "Interrupted", ie); | ||
| } catch (Exception e) { | ||
| logger.warn("[java-lsp] {} failed for workspace {}", method, workspace, e); | ||
| throw new ResponseStatusException(HttpStatus.BAD_GATEWAY, "JDT.LS request failed", e); | ||
| } | ||
| } | ||
|
|
||
| private static String workspaceOf(JsonNode body) { | ||
| return body.path("workspace") | ||
| .asText(null); | ||
| } | ||
|
|
||
| /** Builds {@code {textDocument:{uri}, position:{line,character}}} from the request body. */ | ||
| private ObjectNode positionParams(JsonNode body) { | ||
| ObjectNode params = mapper.createObjectNode(); | ||
| params.putObject("textDocument") | ||
| .put("uri", body.path("uri") | ||
| .asText()); | ||
| params.putObject("position") | ||
| .put("line", body.path("line") | ||
| .asInt()) | ||
| .put("character", body.path("character") | ||
| .asInt()); | ||
| return params; | ||
| } | ||
|
|
||
| /** Builds {@code {item: <hierarchy item>}} from the request body's {@code item} field. */ | ||
| private ObjectNode itemParams(JsonNode body) { | ||
| ObjectNode params = mapper.createObjectNode(); | ||
| params.set("item", body.path("item")); | ||
| return params; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.