diff --git a/ide/spi.debugger.ui/apichanges.xml b/ide/spi.debugger.ui/apichanges.xml index 0703882616a8..90170b2b21ec 100644 --- a/ide/spi.debugger.ui/apichanges.xml +++ b/ide/spi.debugger.ui/apichanges.xml @@ -261,6 +261,21 @@ + + + Source MIME type added to DVFrame. + + + + + +

+ DebuggingView.DVFrame provides source MIME type. +

+
+ +
+ diff --git a/ide/spi.debugger.ui/manifest.mf b/ide/spi.debugger.ui/manifest.mf index c84ce43d54b4..8e22e95684b1 100644 --- a/ide/spi.debugger.ui/manifest.mf +++ b/ide/spi.debugger.ui/manifest.mf @@ -2,6 +2,6 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.spi.debugger.ui/1 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/debugger/ui/Bundle.properties OpenIDE-Module-Layer: org/netbeans/modules/debugger/resources/mf-layer.xml -OpenIDE-Module-Specification-Version: 2.66 +OpenIDE-Module-Specification-Version: 2.67 OpenIDE-Module-Provides: org.netbeans.spi.debugger.ui OpenIDE-Module-Install: org/netbeans/modules/debugger/ui/DebuggerModule.class diff --git a/ide/spi.debugger.ui/src/org/netbeans/spi/debugger/ui/DebuggingView.java b/ide/spi.debugger.ui/src/org/netbeans/spi/debugger/ui/DebuggingView.java index 263297d21a6d..dd085bfc7a1b 100644 --- a/ide/spi.debugger.ui/src/org/netbeans/spi/debugger/ui/DebuggingView.java +++ b/ide/spi.debugger.ui/src/org/netbeans/spi/debugger/ui/DebuggingView.java @@ -477,12 +477,22 @@ public static interface DVFrame { void makeCurrent(); /** - * Gen URI of the source file associated with this frame, if any. + * Get URI of the source file associated with this frame, if any. * @return a source URI, or null if the file is unknown. * @since 2.65 */ URI getSourceURI(); + /** + * Get the source MIME type, if known. + * @return the source MIME type, or null if the source, or + * its MIME type is unknown. + * @since 2.67 + */ + default String getSourceMimeType() { + return null; + } + /** * Line location of the frame in the source code at {@link #getSourceURI()}. * diff --git a/java/debugger.jpda.truffle/nbproject/project.xml b/java/debugger.jpda.truffle/nbproject/project.xml index aff8c0d079a3..5ecb0dd4b03d 100644 --- a/java/debugger.jpda.truffle/nbproject/project.xml +++ b/java/debugger.jpda.truffle/nbproject/project.xml @@ -130,7 +130,7 @@ 1 - 2.65 + 2.67 diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/TruffleAccess.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/TruffleAccess.java index de1ebc81677a..73a116052ad9 100644 --- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/TruffleAccess.java +++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/TruffleAccess.java @@ -89,6 +89,7 @@ public class TruffleAccess implements JPDABreakpointListener { private static final String VAR_FRAME = "frame"; // NOI18N private static final String VAR_SRC_ID = "id"; // NOI18N private static final String VAR_SRC_URI = "uri"; // NOI18N + private static final String VAR_SRC_MIMETYPE = "mimeType"; // NOI18N private static final String VAR_SRC_NAME = "name"; // NOI18N private static final String VAR_SRC_PATH = "path"; // NOI18N private static final String VAR_SRC_SOURCESECTION = "sourceSection"; // NOI18N @@ -308,8 +309,9 @@ public static SourcePosition getSourcePosition(JPDADebugger debugger, ObjectVari String name = (String) sourcePositionVar.getField(VAR_SRC_NAME).createMirrorObject(); String path = (String) sourcePositionVar.getField(VAR_SRC_PATH).createMirrorObject(); URI uri = (URI) sourcePositionVar.getField(VAR_SRC_URI).createMirrorObject(); + String mimeType = (String) sourcePositionVar.getField(VAR_SRC_MIMETYPE).createMirrorObject(); StringReference codeRef = (StringReference) ((JDIVariable) sourcePositionVar.getField(VAR_SRC_CODE)).getJDIValue(); - src = Source.getSource(debugger, id, name, path, uri, codeRef); + src = Source.getSource(debugger, id, name, path, uri, mimeType, codeRef); } return new SourcePosition(debugger, id, src, sourceSection); } @@ -408,6 +410,7 @@ private static SourcePosition parseSource(JPDADebugger debugger, String sourceDe String sourceName; String sourcePath; URI sourceURI; + String mimeType; String sourceSection; try { int i1 = 0; @@ -428,6 +431,9 @@ private static SourcePosition parseSource(JPDADebugger debugger, String sourceDe } i1 = i2 + 1; i2 = sourceDef.indexOf('\n', i1); + mimeType = sourceDef.substring(i1, i2); + i1 = i2 + 1; + i2 = sourceDef.indexOf('\n', i1); if (i2 < 0) { i2 = sourceDef.length(); } @@ -435,7 +441,7 @@ private static SourcePosition parseSource(JPDADebugger debugger, String sourceDe } catch (IndexOutOfBoundsException ioob) { throw new IllegalStateException("var source definition='"+sourceDef+"'", ioob); } - Source src = Source.getSource(debugger, sourceId, sourceName, sourcePath, sourceURI, codeRef); + Source src = Source.getSource(debugger, sourceId, sourceName, sourcePath, sourceURI, mimeType, codeRef); return new SourcePosition(debugger, sourceId, src, sourceSection); } diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/impl/TruffleBreakpointsHandler.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/impl/TruffleBreakpointsHandler.java index 532bba9519ff..0c244e2d657f 100644 --- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/impl/TruffleBreakpointsHandler.java +++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/impl/TruffleBreakpointsHandler.java @@ -21,6 +21,7 @@ import com.sun.jdi.AbsentInformationException; import com.sun.jdi.ArrayReference; +import com.sun.jdi.BooleanValue; import com.sun.jdi.ClassNotLoadedException; import com.sun.jdi.ClassType; import com.sun.jdi.IncompatibleThreadStateException; @@ -192,6 +193,12 @@ public void submitBreakpoints(ClassType accessorClass, ObjectReference debugMana if (bp.isEnabled()) { bpImpl = setLineBreakpoint(debugManager, t, uri, bp.getLineNumber(), getIgnoreCount(bp), bp.getCondition()); + // Find out whether the breakpoint was resolved already during the submission: + try { + updateResolved(bp, bpImpl, t.getThreadReference()); + } catch (Exception ex) { + Exceptions.printStackTrace(Exceptions.attachMessage(ex, "Testing resolved breakpoint at "+uri+":"+bp.getLineNumber())); + } } else { bpImpl = null; } @@ -210,6 +217,15 @@ public void submitBreakpoints(ClassType accessorClass, ObjectReference debugMana } } + private void updateResolved(JSLineBreakpoint breakpoint, ObjectReference bp, ThreadReference tr) throws InternalExceptionWrapper, VMDisconnectedExceptionWrapper, ClassNotPreparedExceptionWrapper, InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException, ObjectCollectedExceptionWrapper { + ClassType breakpointClass = (ClassType) bp.referenceType(); + Method isResolvedMethod = ClassTypeWrapper.concreteMethodByName(breakpointClass, "isResolved", "()Z"); + BooleanValue isResolvedValue = (BooleanValue) ObjectReferenceWrapper.invokeMethod(bp, tr, isResolvedMethod, Collections.emptyList(), ObjectReference.INVOKE_SINGLE_THREADED); + if (isResolvedValue.value()) { + JSBreakpointStatus.setValid(breakpoint, "resolved"); + } + } + private static int getIgnoreCount(JSLineBreakpoint bp) { int ignoreCount = 0; if (Breakpoint.HIT_COUNT_FILTERING_STYLE.GREATER.equals(bp.getHitCountFilteringStyle())) { @@ -301,10 +317,16 @@ public void callMethods(JPDAThread thread) throws InvocationException { ObjectReference.INVOKE_SINGLE_THREADED); ret.disableCollection(); bpRef[0] = ret; + // Find out whether the breakpoint was resolved already during the submission: + for (Value v : ret.getValues()) { + if (v instanceof ObjectReference) { + updateResolved(bp, (ObjectReference) v, tr); + } + } } catch (InvalidTypeException | ClassNotLoadedException | IncompatibleThreadStateException | UnsupportedOperationExceptionWrapper | InternalExceptionWrapper | VMDisconnectedExceptionWrapper | - ObjectCollectedExceptionWrapper ex) { + ObjectCollectedExceptionWrapper | ClassNotPreparedExceptionWrapper ex) { Exceptions.printStackTrace(Exceptions.attachMessage(ex, "Setting breakpoint to "+uri+":"+line)); } finally { persistents.collect(); diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/TruffleStackFrame.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/TruffleStackFrame.java index ac74193a1f26..4f3d176fdfd4 100644 --- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/TruffleStackFrame.java +++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/TruffleStackFrame.java @@ -58,6 +58,7 @@ public final class TruffleStackFrame { private final String sourceName; private final String sourcePath; private final URI sourceURI; + private final String mimeType; private final String sourceSection; private final StringReference codeRef; private TruffleScope[] scopes; @@ -111,6 +112,9 @@ public TruffleStackFrame(JPDADebugger debugger, JPDAThread thread, int depth, throw new IllegalStateException("Bad URI: "+frameDefinition.substring(i1, i2), usex); } i1 = i2 + 1; + i2 = frameDefinition.indexOf('\n', i1); + mimeType = frameDefinition.substring(i1, i2); + i1 = i2 + 1; if (includeInternal) { i2 = frameDefinition.indexOf('\n', i1); sourceSection = frameDefinition.substring(i1, i2); @@ -163,7 +167,7 @@ public String getDisplayName() { public SourcePosition getSourcePosition() { Source src = Source.getExistingSource(debugger, sourceId); if (src == null) { - src = Source.getSource(debugger, sourceId, sourceName, sourcePath, sourceURI, codeRef); + src = Source.getSource(debugger, sourceId, sourceName, sourcePath, sourceURI, mimeType, codeRef); } SourcePosition sp = new SourcePosition(debugger, sourceId, src, sourceSection); return sp; diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/models/TruffleDVFrame.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/models/TruffleDVFrame.java index a194c63cab04..15cf338eae20 100644 --- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/models/TruffleDVFrame.java +++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/models/TruffleDVFrame.java @@ -19,9 +19,11 @@ package org.netbeans.modules.debugger.jpda.truffle.frames.models; import java.net.URI; +import java.net.URISyntaxException; import org.netbeans.modules.debugger.jpda.truffle.access.CurrentPCInfo; import org.netbeans.modules.debugger.jpda.truffle.access.TruffleAccess; import org.netbeans.modules.debugger.jpda.truffle.frames.TruffleStackFrame; +import org.netbeans.modules.debugger.jpda.truffle.source.Source; import org.netbeans.spi.debugger.ui.DebuggingView.DVFrame; import org.netbeans.spi.debugger.ui.DebuggingView.DVThread; @@ -63,7 +65,22 @@ public void makeCurrent() { @Override public URI getSourceURI() { - return truffleFrame.getSourcePosition().getSource().getURI(); + Source source = truffleFrame.getSourcePosition().getSource(); + URI uri = source.getURI(); + if (uri != null && "file".equalsIgnoreCase(uri.getScheme())) { + return uri; + } + try { + return source.getUrl().toURI(); + } catch (URISyntaxException ex) { + return null; + } + } + + @Override + public String getSourceMimeType() { + Source source = truffleFrame.getSourcePosition().getSource(); + return source.getMimeType(); } @Override diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/Source.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/Source.java index 426642f59da3..e40c13f55851 100644 --- a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/Source.java +++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/Source.java @@ -51,10 +51,11 @@ public final class Source { private final String name; private final URI uri; // The original source URI private final URL url; // The source + private final String mimeType; private final long hash; private String content; - private Source(JPDADebugger jpda, String name, URI uri, long hash, StringReference codeRef) { + private Source(JPDADebugger jpda, String name, URI uri, String mimeType, long hash, StringReference codeRef) { this.name = name; this.codeRef = codeRef; URL url = null; @@ -76,6 +77,7 @@ private Source(JPDADebugger jpda, String name, URI uri, long hash, StringReferen } this.url = url; this.uri = uri; + this.mimeType = mimeType; this.hash = hash; } @@ -119,6 +121,15 @@ public static Source getSource(JPDADebugger debugger, long id, String path, URI uri, StringReference codeRef) { + return getSource(debugger, id, name, path, uri, null, codeRef); + } + + public static Source getSource(JPDADebugger debugger, long id, + String name, + String path, + URI uri, + String mimeType, + StringReference codeRef) { synchronized (KNOWN_SOURCES) { Map dbgSources = KNOWN_SOURCES.get(debugger); if (dbgSources != null) { @@ -128,16 +139,17 @@ public static Source getSource(JPDADebugger debugger, long id, } } } - return getTheSource(debugger, id, name, path, uri, codeRef); + return getTheSource(debugger, id, name, path, uri, mimeType, codeRef); } private static Source getTheSource(JPDADebugger debugger, long id, String name, String path, URI uri, + String mimeType, StringReference codeRef) { - Source src = new Source(debugger, name, uri, id, codeRef); + Source src = new Source(debugger, name, uri, mimeType, id, codeRef); synchronized (KNOWN_SOURCES) { Map dbgSources = KNOWN_SOURCES.get(debugger); if (dbgSources == null) { @@ -161,6 +173,10 @@ public URI getURI() { return uri; } + public String getMimeType() { + return mimeType; + } + public long getHash() { return hash; } diff --git a/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/FrameInfo.java b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/FrameInfo.java index 61c7167540a6..c9466c0d11a2 100644 --- a/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/FrameInfo.java +++ b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/FrameInfo.java @@ -41,7 +41,7 @@ final class FrameInfo { FrameInfo(DebugStackFrame topStackFrame, Iterable stackFrames) { SourceSection topSS = topStackFrame.getSourceSection(); - SourcePosition position = new SourcePosition(topSS); + SourcePosition position = new SourcePosition(topSS, topStackFrame.getLanguage()); ArrayList stackFramesArray = new ArrayList<>(); for (DebugStackFrame sf : stackFrames) { if (sf == topStackFrame) { @@ -61,7 +61,7 @@ final class FrameInfo { ((sfLang != null) ? sfLang.getId() + " " + sfLang.getName() : "") + "\n" + DebuggerVisualizer.getSourceLocation(topSS) + "\n" + position.id + "\n" + position.name + "\n" + position.path + "\n" + - position.uri.toString() + "\n" + position.sourceSection +/* "," + position.startColumn + "," + + position.uri.toString() + "\n" + position.mimeType + "\n" + position.sourceSection +/* "," + position.startColumn + "," + position.endLine + "," + position.endColumn +*/ "\n" + isInternal(topStackFrame); topVariables = JPDATruffleAccessor.getVariables(topStackFrame); } diff --git a/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/GuestObject.java b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/GuestObject.java index 8b159a186380..dedb85251f7e 100644 --- a/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/GuestObject.java +++ b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/GuestObject.java @@ -145,7 +145,7 @@ public final class GuestObject { SourceSection sourceLocation = value.getSourceLocation(); //System.err.println("\nSOURCE of "+value.getName()+" is: "+sourceLocation); if (sourceLocation != null && sourceLocation.isAvailable()) { - sp = new SourcePosition(sourceLocation); + sp = new SourcePosition(sourceLocation, value.getOriginalLanguage()); } } catch (ThreadDeath td) { throw td; @@ -159,7 +159,7 @@ public final class GuestObject { SourceSection sourceLocation = metaObject.getSourceLocation(); //System.err.println("\nSOURCE of metaobject "+metaObject+" is: "+sourceLocation); if (sourceLocation != null && sourceLocation.isAvailable()) { - sp = new SourcePosition(sourceLocation); + sp = new SourcePosition(sourceLocation, value.getOriginalLanguage()); } } catch (ThreadDeath td) { throw td; diff --git a/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/JPDATruffleAccessor.java b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/JPDATruffleAccessor.java index 2de2dd056822..65cc377f820e 100644 --- a/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/JPDATruffleAccessor.java +++ b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/JPDATruffleAccessor.java @@ -231,7 +231,7 @@ static Object[] getFramesInfo(DebugStackFrame[] frames, boolean includeInternal) System.err.println("frameInfos = "+frameInfos); *//* }*/ - SourcePosition position = new SourcePosition(sf.getSourceSection()); + SourcePosition position = new SourcePosition(sf.getSourceSection(), sf.getLanguage()); frameInfos.append(createPositionIdentificationString(position)); if (includeInternal) { frameInfos.append('\n'); @@ -261,6 +261,8 @@ private static String createPositionIdentificationString(SourcePosition position str.append('\n'); str.append(position.uri.toString()); str.append('\n'); + str.append(position.mimeType); + str.append('\n'); str.append(position.sourceSection); return str.toString(); } diff --git a/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/JPDATruffleDebugManager.java b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/JPDATruffleDebugManager.java index f3fd297071cb..4702f4a9eef9 100644 --- a/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/JPDATruffleDebugManager.java +++ b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/JPDATruffleDebugManager.java @@ -107,7 +107,7 @@ public void onSuspend(SuspendedEvent event) { } suspendedEvents.set(event); try { - SourcePosition position = new SourcePosition(event.getSourceSection()); + SourcePosition position = new SourcePosition(event.getSourceSection(), event.getTopStackFrame().getLanguage()); int stepCmd = JPDATruffleAccessor.executionHalted( this, position, event.getSuspendAnchor() == SuspendAnchor.BEFORE, diff --git a/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/SourcePosition.java b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/SourcePosition.java index d4321b0f991d..e4c782bc627d 100644 --- a/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/SourcePosition.java +++ b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/SourcePosition.java @@ -19,6 +19,7 @@ package org.netbeans.modules.debugger.jpda.backend.truffle; +import com.oracle.truffle.api.nodes.LanguageInfo; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; @@ -41,8 +42,9 @@ final class SourcePosition { final String sourceSection; final String code; final URI uri; + final String mimeType; - public SourcePosition(SourceSection sourceSection) { + public SourcePosition(SourceSection sourceSection, LanguageInfo languageInfo) { Source source = sourceSection.getSource(); this.id = getId(source); this.name = source.getName(); @@ -54,6 +56,15 @@ public SourcePosition(SourceSection sourceSection) { this.sourceSection = sourceSection.getStartLine() + "," + sourceSection.getStartColumn() + "," + sourceSection.getEndLine() + "," + sourceSection.getEndColumn(); this.code = source.getCharacters().toString(); this.uri = source.getURI(); + this.mimeType = findMIMEType(source, languageInfo); + } + + private String findMIMEType(Source source, LanguageInfo languageInfo) { + String mimeType = source.getMimeType(); + if (mimeType == null && languageInfo != null) { + mimeType = languageInfo.getDefaultMimeType(); + } + return mimeType; } private static synchronized long getId(Source s) { diff --git a/java/java.lsp.server/nbproject/project.xml b/java/java.lsp.server/nbproject/project.xml index 1dc3a47aef80..99a5144c182b 100644 --- a/java/java.lsp.server/nbproject/project.xml +++ b/java/java.lsp.server/nbproject/project.xml @@ -292,7 +292,7 @@ 1 - 2.63 + 2.67 diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/DebugAdapterContext.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/DebugAdapterContext.java index 73c6dc215094..5d0491a4d2ba 100644 --- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/DebugAdapterContext.java +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/DebugAdapterContext.java @@ -33,6 +33,7 @@ import org.netbeans.modules.java.lsp.server.debugging.breakpoints.BreakpointsManager; import org.netbeans.modules.java.lsp.server.debugging.launch.NbDebugSession; +import org.openide.util.Pair; public final class DebugAdapterContext { @@ -51,7 +52,8 @@ public final class DebugAdapterContext { private boolean isDebugMode = true; private final AtomicInteger lastSourceReferenceId = new AtomicInteger(0); - private final Map sourceReferences = new ConcurrentHashMap<>(); + private final Map> sourcesById = new ConcurrentHashMap<>(); + private final Map sourceReferences = new ConcurrentHashMap<>(); private final NBConfigurationSemaphore configurationSemaphore = new NBConfigurationSemaphore(); private final NbSourceProvider sourceProvider = new NbSourceProvider(this); @@ -179,13 +181,27 @@ public void setSourcePaths(String[] sourcePaths) { this.sourcePaths = sourcePaths; } - public String getSourceUri(int sourceReference) { - return sourceReferences.get(sourceReference); + public URI getSourceUri(int sourceReference) { + Pair sourceInfo = sourcesById.get(sourceReference); + if (sourceInfo != null) { + return sourceInfo.first(); + } else { + return null; + } + } + + public String getSourceMimeType(int sourceReference) { + Pair sourceInfo = sourcesById.get(sourceReference); + if (sourceInfo != null) { + return sourceInfo.second(); + } else { + return null; + } } - public int createSourceReference(String uri) { - int id = lastSourceReferenceId.incrementAndGet(); - sourceReferences.put(id, uri); + public int createSourceReference(URI uri, String mimeType) { + int id = sourceReferences.computeIfAbsent(uri, u -> lastSourceReferenceId.incrementAndGet()); + sourcesById.put(id, Pair.of(uri, mimeType)); return id; } diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/NbProtocolServer.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/NbProtocolServer.java index cb2eff9dae35..73d83fc0d659 100644 --- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/NbProtocolServer.java +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/NbProtocolServer.java @@ -18,7 +18,11 @@ */ package org.netbeans.modules.java.lsp.server.debugging; +import java.io.File; +import java.net.MalformedURLException; import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; @@ -286,11 +290,33 @@ public CompletableFuture stackTrace(StackTraceArguments args stackFrame.setId(frameId); stackFrame.setName(frame.getName()); URI sourceURI = frame.getSourceURI(); - if (sourceURI != null && sourceURI.getPath() != null) { + if (sourceURI != null) { Source source = new Source(); - source.setName(Paths.get(sourceURI).getFileName().toString()); - source.setPath(sourceURI.getPath()); - source.setSourceReference(0); + String scheme = sourceURI.getScheme(); + if (null == scheme || scheme.isEmpty() || "file".equalsIgnoreCase(scheme)) { + source.setName(Paths.get(sourceURI).getFileName().toString()); + source.setPath(sourceURI.getPath()); + source.setSourceReference(0); + } else { + int ref = context.createSourceReference(sourceURI, frame.getSourceMimeType()); + String path = sourceURI.getPath(); + if (path == null) { + path = sourceURI.getSchemeSpecificPart(); + } + if (path != null) { + int sepIndex = Math.max(path.lastIndexOf('/'), path.lastIndexOf(File.separatorChar)); + source.setName(path.substring(sepIndex + 1)); + if ("jar".equalsIgnoreCase(scheme)) { + try { + path = new URI(path).getPath(); + } catch (URISyntaxException ex) { + // ignore, we just tried + } + } + source.setPath(path); + } + source.setSourceReference(ref); + } stackFrame.setSource(source); } stackFrame.setLine(line); @@ -343,10 +369,10 @@ public CompletableFuture source(SourceArguments args) { if (sourceReference <= 0) { ErrorUtilities.completeExceptionally(future, "SourceRequest: property 'sourceReference' is missing, null, or empty", ResponseErrorCode.InvalidParams); } else { - String uri = context.getSourceUri(sourceReference); + URI uri = context.getSourceUri(sourceReference); NbSourceProvider sourceProvider = context.getSourceProvider(); SourceResponse response = new SourceResponse(); - response.setMimeType("text/x-java"); // Set mimeType to tell clients to recognize the source contents as java source + response.setMimeType(context.getSourceMimeType(sourceReference)); response.setContent(sourceProvider.getSourceContents(uri)); future.complete(response); } diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/NbSourceProvider.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/NbSourceProvider.java index e4d792b8eb28..630324820ba3 100644 --- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/NbSourceProvider.java +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/NbSourceProvider.java @@ -19,7 +19,13 @@ package org.netbeans.modules.java.lsp.server.debugging; import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.MalformedURLException; +import java.net.URI; import java.net.URISyntaxException; +import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -67,9 +73,25 @@ private String getSourceFileURI(String fqn, String relativePathName) { }); } - public String getSourceContents(String arg0) { - LOG.log(Level.INFO, "SourceContent {0}", arg0); - throw new UnsupportedOperationException("Not supported yet."); + public String getSourceContents(URI uri) { + LOG.log(Level.INFO, "SourceContent {0}", uri); + URL url; + try { + url = uri.toURL(); + } catch (MalformedURLException ex) { + return ex.getLocalizedMessage(); + } + StringBuilder content = new StringBuilder(); + char[] buffer = new char[8192]; + try (Reader r = new InputStreamReader(url.openConnection().getInputStream())) { + int l; + while ((l = r.read(buffer)) > 0) { + content.append(buffer, 0, l); + } + } catch (IOException ex) { + return ex.getLocalizedMessage(); + } + return content.toString(); } public Source getSource(String sourceName, String debuggerURI) { diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/breakpoints/NbBreakpoint.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/breakpoints/NbBreakpoint.java index 2cfef0506e28..0a36344bce71 100644 --- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/breakpoints/NbBreakpoint.java +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/breakpoints/NbBreakpoint.java @@ -19,6 +19,7 @@ package org.netbeans.modules.java.lsp.server.debugging.breakpoints; import java.net.MalformedURLException; +import java.net.URI; import java.net.URL; import java.util.HashMap; import java.util.Map; @@ -35,6 +36,7 @@ import org.netbeans.api.debugger.jpda.LineBreakpoint; import org.netbeans.modules.debugger.jpda.truffle.breakpoints.TruffleLineBreakpoint; import org.netbeans.modules.java.lsp.server.debugging.DebugAdapterContext; +import org.openide.filesystems.URLMapper; /** * @@ -56,6 +58,15 @@ public final class NbBreakpoint { public NbBreakpoint(Source source, String sourceURL, int line, int hitCount, String condition, String logMessage, DebugAdapterContext context) { this.source = source; + Integer ref = source.getSourceReference(); + if (ref != null && ref != 0) { + URI uri = context.getSourceUri(ref); + if (uri != null) { + try { + sourceURL = uri.toURL().toString(); + } catch (MalformedURLException ex) {} + } + } this.sourceURL = sourceURL; this.line = line; this.hitCount = hitCount; @@ -116,9 +127,9 @@ public CompletableFuture install() { breakpoint.addPropertyChangeListener(Breakpoint.PROP_VALIDITY, evt -> { updateValid(breakpoint, true); }); - updateValid(breakpoint, false); DebuggerManager d = DebuggerManager.getDebuggerManager(); d.addBreakpoint(breakpoint); + updateValid(breakpoint, false); this.breakpoint = breakpoint; return CompletableFuture.completedFuture(this); } diff --git a/java/java.lsp.server/vscode/package.json b/java/java.lsp.server/vscode/package.json index 0cae9ddd240b..ee7d99fa7668 100644 --- a/java/java.lsp.server/vscode/package.json +++ b/java/java.lsp.server/vscode/package.json @@ -163,7 +163,7 @@ "watch": "tsc -watch -p ./", "test": "node ./out/test/runTest.js", "nbcode": "node ./out/nbcode.js", - "nbjavac" : "node ./out/nbcode.js -J-Dnetbeans.close=true --modules --install .*nbjavac.*" + "nbjavac": "node ./out/nbcode.js -J-Dnetbeans.close=true --modules --install .*nbjavac.*" }, "devDependencies": { "@types/vscode": "^1.47.0",