You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@netbeans.apache.org by "lahodaj (via GitHub)" <gi...@apache.org> on 2024/12/05 13:56:59 UTC

[PR] Prototype for inlining values inside debugger [netbeans]

lahodaj opened a new pull request, #8019:
URL: https://github.com/apache/netbeans/pull/8019

   This is a prototype of a feature to show variable values directly in the editor, as permitted by the LSP protocol.
   
   It looks like this inside VS Code:
   ![inline-values-vscode](https://github.com/user-attachments/assets/726395f1-5de0-415a-a7bc-e602dc6026ec)
   
   and inside NetBeans:
   ![inline-values-nb](https://github.com/user-attachments/assets/b72c2a7a-1a59-4472-beca-5929eef335fe)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Prototype for inlining values inside debugger [netbeans]

Posted by "lahodaj (via GitHub)" <gi...@apache.org>.
lahodaj commented on PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#issuecomment-2727565035

   I did a lot of improvements lately. Lets see if this is heading in the right direction.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Prototype for inlining values inside debugger [netbeans]

Posted by "lahodaj (via GitHub)" <gi...@apache.org>.
lahodaj commented on PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#issuecomment-2727565583

   FWIW, in VSCode, this needs to be enabled explicitly using the:
   ```
   "debug.inlineValues": "on"
   ```
   setting. I didn't find a way, yet, to make it work with `auto`.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Prototype for inlining values inside debugger [netbeans]

Posted by "lahodaj (via GitHub)" <gi...@apache.org>.
lahodaj commented on PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#issuecomment-2743526170

   Thanks for testing. I'll look at the exception.
   
   For the toggle - I think I would prefer to have a different toggle (people may want the debugger values, but not parameter hints, and maybe even vice versa). I'll look into that.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Inlining values inside debugger [netbeans]

Posted by "lahodaj (via GitHub)" <gi...@apache.org>.
lahodaj commented on code in PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#discussion_r2095126376


##########
ide/api.lsp/src/org/netbeans/api/lsp/InlineValue.java:
##########
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.api.lsp;
+
+/**
+ * An expression whose value may be show inline while debugging.
+ *
+ * @since 1.35
+ */
+public final class InlineValue {

Review Comment:
   Right now, I am a bit careful about adding a record as an API, as adding/removing components is not compatible at this time. (When deconstructors are added, this may change, of course.)



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Inlining values inside debugger [netbeans]

Posted by "lahodaj (via GitHub)" <gi...@apache.org>.
lahodaj commented on PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#issuecomment-2912148469

   Unless there are objections, I would like to merge sometime soon - probably today or tomorrow.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Prototype for inlining values inside debugger [netbeans]

Posted by "neilcsmith-net (via GitHub)" <gi...@apache.org>.
neilcsmith-net commented on PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#issuecomment-2789766811

   Aside from commit cleanup (cause of failing test) is this close to ready for merging, or are we retargetting for NB27?


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Inlining values inside debugger [netbeans]

Posted by "lahodaj (via GitHub)" <gi...@apache.org>.
lahodaj commented on code in PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#discussion_r2095126949


##########
java/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/ui/models/InlineValueComputerImpl.java:
##########
@@ -0,0 +1,477 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.debugger.jpda.ui.models;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.prefs.PreferenceChangeEvent;
+import java.util.prefs.PreferenceChangeListener;
+import java.util.prefs.Preferences;
+import java.util.stream.Collectors;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Document;
+import org.netbeans.api.debugger.DebuggerManagerAdapter;
+import org.netbeans.api.debugger.LazyDebuggerManagerListener;
+import org.netbeans.api.debugger.Session;
+import org.netbeans.api.debugger.jpda.CallStackFrame;
+import org.netbeans.api.debugger.jpda.InvalidExpressionException;
+import org.netbeans.api.debugger.jpda.JPDAClassType;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.ObjectVariable;
+import org.netbeans.api.debugger.jpda.Variable;
+import org.netbeans.api.editor.mimelookup.MimeLookup;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.editor.settings.AttributesUtilities;
+import org.netbeans.api.java.source.CancellableTask;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.JavaSource.Phase;
+import org.netbeans.api.java.source.JavaSource.Priority;
+import org.netbeans.api.java.source.JavaSourceTaskFactory;
+import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
+import org.netbeans.modules.debugger.jpda.expr.formatters.Formatters;
+import org.netbeans.modules.debugger.jpda.expr.formatters.FormattersLoopControl;
+import org.netbeans.modules.debugger.jpda.expr.formatters.VariablesFormatter;
+import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.InvalidStackFrameExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ObjectCollectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.ui.values.ComputeInlineValues;
+import org.netbeans.modules.debugger.jpda.ui.values.ComputeInlineValues.InlineVariable;
+import org.netbeans.modules.parsing.spi.TaskIndexingMode;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.debugger.ui.Constants;
+import org.netbeans.spi.editor.highlighting.HighlightsLayer;
+import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory;
+import org.netbeans.spi.editor.highlighting.HighlightsSequence;
+import org.netbeans.spi.editor.highlighting.ZOrder;
+import org.netbeans.spi.editor.highlighting.support.OffsetsBag;
+import org.openide.cookies.EditorCookie;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.RequestProcessor;
+import org.openide.util.WeakListeners;
+import org.openide.util.lookup.ServiceProvider;
+import org.openide.util.lookup.ServiceProviders;
+
+
+@DebuggerServiceRegistration(path="netbeans-JPDASession/inlineValue", types=InlineValueComputer.class)
+public class InlineValueComputerImpl implements InlineValueComputer, PreferenceChangeListener, PropertyChangeListener {
+
+    private static final Logger LOG = Logger.getLogger(InlineValueComputerImpl.class.getName());
+    private static final RequestProcessor EVALUATOR = new RequestProcessor(InlineValueComputerImpl.class.getName(), 1, false, false);
+    private static final String JAVA_STRATUM = "Java"; //XXX: this is probably already defined somewhere
+    private final JPDADebuggerImpl debugger;
+    private final Preferences prefs;
+    private TaskDescription currentTask;
+
+    public InlineValueComputerImpl(ContextProvider contextProvider) {
+        debugger = (JPDADebuggerImpl) contextProvider.lookupFirst(null, JPDADebugger.class);
+        debugger.addPropertyChangeListener(this);
+        prefs = MimeLookup.getLookup("text/x-java").lookup(Preferences.class);
+        prefs.addPreferenceChangeListener(WeakListeners.create(PreferenceChangeListener.class, this, prefs));
+    }
+
+    @Override
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (JPDADebugger.PROP_STATE.equals(evt.getPropertyName()) &&
+            debugger.getState() == JPDADebugger.STATE_DISCONNECTED) {
+            setNewTask(null);
+        }
+
+        if (JPDADebugger.PROP_CURRENT_CALL_STACK_FRAME.equals(evt.getPropertyName())) {
+            refreshVariables();
+        }
+    }
+
+    @Override
+    public void preferenceChange(PreferenceChangeEvent evt) {
+        refreshVariables();
+    }
+
+    private void refreshVariables() {
+        CallStackFrame frame = debugger.getCurrentCallStackFrame();
+
+        FileObject frameFile = null;
+        int frameLineNumber = -1;
+        Document frameDocument = null;
+
+        if (prefs.getBoolean(Constants.KEY_INLINE_VALUES, Constants.DEF_INLINE_VALUES) &&
+            frame != null && !frame.isObsolete() &&
+            frame.getThread().isSuspended() &&
+            JAVA_STRATUM.equals(frame.getDefaultStratum())) {
+            try {
+                String url = debugger.getEngineContext().getURL(frame, JAVA_STRATUM);
+                frameFile = url != null ? URLMapper.findFileObject(URI.create(url).toURL()) : null;
+                if (frameFile != null) {
+                    frameLineNumber = frame.getLineNumber(JAVA_STRATUM);
+                    EditorCookie ec = frameFile.getLookup().lookup(EditorCookie.class);
+                    frameDocument = ec != null ? ec.getDocument() : null;
+                }
+            } catch (InternalExceptionWrapper | InvalidStackFrameExceptionWrapper | ObjectCollectedExceptionWrapper | VMDisconnectedExceptionWrapper | MalformedURLException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        }
+
+        TaskDescription newTask;
+
+        if (frameFile != null && frameDocument != null) {
+            newTask = new TaskDescription(frameFile, frameLineNumber, frameDocument);
+        } else {
+            newTask = null;
+        }
+
+        if (setNewTask(newTask)) {
+            return;
+        }
+
+        if (newTask != null) {
+            //TODO: cancel any already running computation if the configuration is different:
+            CountDownLatch computationDone = new CountDownLatch(1);
+
+            newTask.addCancelCallback(computationDone::countDown);
+
+            AtomicReference<Collection<InlineVariable>> values = new AtomicReference<>();
+
+            EVALUATOR.post(() -> {
+                OffsetsBag runningBag = new OffsetsBag(newTask.frameDocument);
+
+                Lookup.getDefault().lookup(ComputeInlineVariablesFactory.class).set(newTask.frameFile, newTask.frameLineNumber, variables -> {
+                    values.set(variables);
+                    computationDone.countDown();
+                });
+
+                try {
+                    computationDone.await();
+                } catch (InterruptedException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+
+                if (newTask.isCancelled()) {
+                    return ;
+                }
+
+                Collection<InlineVariable> variables = values.get();
+
+                if (values == null) {
+                    return ;
+                }
+
+                Map<String, Variable> expression2Value = new HashMap<>();
+                Map<Integer, Map<String, String>> line2Values = new HashMap<>();
+
+                for (InlineVariable v : variables) {
+                    if (newTask.isCancelled()) {
+                        return ;
+                    }
+
+                    Variable value = expression2Value.computeIfAbsent(v.expression, expr -> {
+                        try {
+                            return debugger.evaluate(expr);
+                        } catch (InvalidExpressionException ex) {
+                            //the variable may not exist
+                            LOG.log(Level.FINE, null, ex);
+                            return null;
+                        }
+                    });
+                    if (value != null) {
+                        String valueText;
+                        if (value instanceof ObjectVariable ov) {
+                            valueText = toValue(ov).replace("\n", "\\n");
+                        } else {
+                            valueText = value.getValue();
+                        }
+                        line2Values.computeIfAbsent(v.lineEnd, __ -> new LinkedHashMap<>())
+                                   .putIfAbsent(v.expression, v.expression + " = " + valueText);
+                        String mergedValues = line2Values.get(v.lineEnd).values().stream().collect(Collectors.joining(", ", "  ", ""));
+                        AttributeSet attrs = AttributesUtilities.createImmutable("virtual-text-prepend", mergedValues);
+
+                        runningBag.addHighlight(v.lineEnd, v.lineEnd + 1, attrs);
+
+                        setHighlights(newTask, runningBag);
+                    }
+                }
+            });
+        }
+    }
+
+    private String toValue(ObjectVariable variable) {
+        //mostly copied from the VariablesFormatterFilter.getValueAt:
+        FormattersLoopControl formattersLoop = new FormattersLoopControl();
+        String type = variable.getType ();
+        ObjectVariable ov = (ObjectVariable) variable;
+        JPDAClassType ct = ov.getClassType();
+        if (ct == null) {
+            return ov.getValue();
+        }
+        VariablesFormatter f = Formatters.getFormatterForType(ct, formattersLoop.getFormatters());
+        String[] formattersInLoopRef = new String[] { null };
+        if (f != null && formattersLoop.canUse(f, ct.getName(), formattersInLoopRef)) {
+            String code = f.getValueFormatCode();
+            if (code != null && code.length() > 0) {
+                try {
+                    java.lang.reflect.Method evaluateMethod = ov.getClass().getMethod("evaluate", String.class);
+                    evaluateMethod.setAccessible(true);
+                    Variable ret = (Variable) evaluateMethod.invoke(ov, code);
+                    if (ret == null) {
+                        return null;
+                    }
+                    return ret.getValue();
+                } catch (java.lang.reflect.InvocationTargetException itex) {
+                    Throwable t = itex.getTargetException();
+                    if (t instanceof InvalidExpressionException) {
+                        return ov.getValue();
+                    } else {
+                        Exceptions.printStackTrace(t);
+                    }
+                } catch (NoSuchMethodException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (SecurityException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (IllegalAccessException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (IllegalArgumentException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+            }
+        } else if (formattersInLoopRef[0] != null) {
+            //ignore loops(?)
+        }
+        if (VariablesFormatterFilter.isToStringValueType(type)) {
+            try {
+                return "\""+ov.getToStringValue ()+"\"";
+            } catch (InvalidExpressionException ex) {
+                // Not a supported operation (e.g. J2ME, see #45543)
+                // Or missing context or any other reason
+                Logger.getLogger(VariablesFormatterFilter.class.getName()).fine("getToStringValue() "+ex.getLocalizedMessage());
+                if ( (ex.getTargetException () != null) &&
+                     (ex.getTargetException () instanceof
+                       UnsupportedOperationException)
+                ) {
+                    // PATCH for J2ME. see 45543
+                    return ov.getValue();
+                }
+                return ex.getLocalizedMessage ();
+            }
+        }
+        return ov.getValue();
+    }
+
+    private synchronized boolean setNewTask(TaskDescription newTask) {
+        if (Objects.equals(currentTask, newTask)) {
+            return true; //nothing changed, nothing to do
+        }
+
+        if (currentTask != null) {
+            currentTask.cancel();
+            getHighlightsBag(currentTask.frameDocument).clear();
+        }
+
+        currentTask = newTask;
+
+        return false;
+    }
+
+    private synchronized void setHighlights(TaskDescription task, OffsetsBag highlights) {
+        if (!task.isCancelled()) {
+            getHighlightsBag(currentTask.frameDocument).setHighlights(highlights);
+        }
+    }
+
+    @DebuggerServiceRegistration(types=LazyDebuggerManagerListener.class)
+    public static final class Init extends DebuggerManagerAdapter {
+        @Override
+        public void sessionAdded(Session session) {
+            for (InlineValueComputer v : session.lookup("inlineValue", InlineValueComputer.class)) {
+            }

Review Comment:
   The cancel is happening in `setNewTask`, todo removed.



##########
ide/spi.debugger.ui/apichanges.xml:
##########
@@ -51,6 +51,23 @@
 <!-- ACTUAL CHANGES BEGIN HERE: -->
 
 <changes>
+    <change id="inline-values-setting-keys">
+        <api name="DebuggerCoreSPI"/>
+        <summary><code>Constants</code> class enhanced with constants for inline values.</summary>
+        <version major="2" minor="88"/>
+        <date day="8" month="4" year="2025"/>
+        <author login="jlahoda"/>
+        <compatibility binary="compatible" source="compatible" modification="yes" semantic="compatible"/>
+        <description>
+            <p>
+                Two new contants are added to <code>Constants</code>:

Review Comment:
   Done.



##########
ide/api.lsp/src/org/netbeans/api/lsp/InlineValue.java:
##########
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.api.lsp;
+
+/**
+ * An expression whose value may be show inline while debugging.

Review Comment:
   Done.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Prototype for inlining values inside debugger [netbeans]

Posted by "neilcsmith-net (via GitHub)" <gi...@apache.org>.
neilcsmith-net commented on PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#issuecomment-2743544426

   I like where this is going, and agree that this shouldn't be aligned with the inline hints toggle but something separately configured.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Inlining values inside debugger [netbeans]

Posted by "lahodaj (via GitHub)" <gi...@apache.org>.
lahodaj merged PR #8019:
URL: https://github.com/apache/netbeans/pull/8019


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Inlining values inside debugger [netbeans]

Posted by "mbien (via GitHub)" <gi...@apache.org>.
mbien commented on PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#issuecomment-2890866495

   got the same NPE again while testing (https://github.com/apache/netbeans/pull/8019#issuecomment-2741293647):
   
   <details>
   
   ```
   java.lang.NullPointerException: Cannot invoke "com.sun.source.util.TreePath.getLeaf()" because "path" is null
   	at com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:66)
   	at org.netbeans.modules.debugger.jpda.ui.values.ComputeInlineValues.computeVariables(ComputeInlineValues.java:113)
   	at org.netbeans.modules.debugger.jpda.ui.models.InlineValueComputerImpl$ComputeInlineVariablesFactory$1.run(InlineValueComputerImpl.java:361)
   	at org.netbeans.modules.debugger.jpda.ui.models.InlineValueComputerImpl$ComputeInlineVariablesFactory$1.run(InlineValueComputerImpl.java:338)
   [catch] at org.netbeans.modules.java.source.JavaSourceAccessor$CancelableTaskWrapper.run(JavaSourceAccessor.java:273)
   	at org.netbeans.modules.parsing.impl.TaskProcessor.callParserResultTask(TaskProcessor.java:561)
   	at org.netbeans.modules.parsing.impl.TaskProcessor$RequestPerformer.run(TaskProcessor.java:786)
   	at org.openide.util.lookup.Lookups.executeWith(Lookups.java:288)
   	at org.netbeans.modules.parsing.impl.TaskProcessor$RequestPerformer.execute(TaskProcessor.java:702)
   	at org.netbeans.modules.parsing.impl.TaskProcessor$CompilationJob.run(TaskProcessor.java:663)
   	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545)
   	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328)
   	at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:1403)
   	at org.netbeans.modules.openide.util.GlobalLookup.execute(GlobalLookup.java:45)
   	at org.openide.util.lookup.Lookups.executeWith(Lookups.java:287)
   	at org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:2018)
   ```
   
   </details>
   
   
   > I tried to reproduce, but does not happen for me. Is there something specific you do? (I did a merge, so there's a possibility that fixed it, although I'd be surprised if it did.)
   
   Oops, I just noticed that this problem is already in NB 26 - so we can probably ignore it here since it is not a regression:
   ![image](https://github.com/user-attachments/assets/8fad8181-1045-431d-813d-7d511469a461)
   
   I don't have a reliable reproducer yet unfortunately. At some point the `View` menu can get out-of-sync if the state is toggled by other means, e.g `alt-I` hotkey or the checkbox in `options -> inline hints`. The same happened while testing the new value hints - but it turns out its nothing new.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Inlining values inside debugger [netbeans]

Posted by "lahodaj (via GitHub)" <gi...@apache.org>.
lahodaj commented on PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#issuecomment-2890070354

   > there seems to be a problem where the inline hints state and the displayed values in the UI are out of sync.
   > 
   > When I press `alt+I` the checkbox in the view menu (and options) don't update to the new value, even though the inline hints do toggle within the editor. The UI does still work, but it can become inverted with the editor state.
   > 
   > This is a bit of a dejavu since I think this happened before while working on a prior inline hint PR - but I can't remember what the problem was.
   
   I tried to reproduce, but does not happen for me. Is there something specific you do? (I did a merge, so there's a possibility that fixed it, although I'd be surprised if it did.)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Prototype for inlining values inside debugger [netbeans]

Posted by "mbien (via GitHub)" <gi...@apache.org>.
mbien commented on PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#issuecomment-2741293647

   gave it another try and it worked great. I got one exception once but wasn't able to reproduce it a second time:
   
   <details>
   
   ```
   java.lang.NullPointerException: Cannot invoke "com.sun.source.util.TreePath.getLeaf()" because "path" is null
   	at com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:66)
   	at org.netbeans.modules.debugger.jpda.ui.values.ComputeInlineValues.computeVariables(ComputeInlineValues.java:115)
   	at org.netbeans.modules.debugger.jpda.ui.models.InlineValueComputerImpl$ComputeInlineVariablesFactory$1.run(InlineValueComputerImpl.java:344)
   	at org.netbeans.modules.debugger.jpda.ui.models.InlineValueComputerImpl$ComputeInlineVariablesFactory$1.run(InlineValueComputerImpl.java:321)
   [catch] at org.netbeans.modules.java.source.JavaSourceAccessor$CancelableTaskWrapper.run(JavaSourceAccessor.java:273)
   	at org.netbeans.modules.parsing.impl.TaskProcessor.callParserResultTask(TaskProcessor.java:561)
   	at org.netbeans.modules.parsing.impl.TaskProcessor$RequestPerformer.run(TaskProcessor.java:786)
   	at org.openide.util.lookup.Lookups.executeWith(Lookups.java:288)
   	at org.netbeans.modules.parsing.impl.TaskProcessor$RequestPerformer.execute(TaskProcessor.java:702)
   	at org.netbeans.modules.parsing.impl.TaskProcessor$CompilationJob.run(TaskProcessor.java:663)
   	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
   	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
   	at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:1403)
   	at org.netbeans.modules.openide.util.GlobalLookup.execute(GlobalLookup.java:45)
   	at org.openide.util.lookup.Lookups.executeWith(Lookups.java:287)
   	at org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:2018)
   ```
   
   </details>
   
   the inline hint should also listen to the main on/off toggle and be potentially also configurable like its other friends (see options window):
   ![image](https://github.com/user-attachments/assets/ccd882f9-a7c2-4a84-8430-df26ac483ba2)
   
   (I think listening to the main toggle would be sufficient, finer grained options could be added when needed in followup PRs - we don't have to solve everything at once)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Prototype for inlining values inside debugger [netbeans]

Posted by "mbien (via GitHub)" <gi...@apache.org>.
mbien commented on PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#issuecomment-2743557894

   What I had in mind is that I think it would be nice to retain the master switch for all inline hints, but add more shortcuts for the sub-switches. It could be somewhere in the debugger toolbar too potentially. Completely decoupled from the editor setting could also work - not sure what is more intuitive.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Prototype for inlining values inside debugger [netbeans]

Posted by "lahodaj (via GitHub)" <gi...@apache.org>.
lahodaj commented on PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#issuecomment-2804909286

   Moving to NB 27, although assuming I can stabilize tests, I would like to integrate as early as possible (so that there's time to fix any breakages).


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Inlining values inside debugger [netbeans]

Posted by "lahodaj (via GitHub)" <gi...@apache.org>.
lahodaj commented on PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#issuecomment-2894825221

   I've fixed the NPE (hopefully), and squashed (to hopefully get a clean GH Actions). I wonder what are the current impressions on this?


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Inlining values inside debugger [netbeans]

Posted by "lahodaj (via GitHub)" <gi...@apache.org>.
lahodaj commented on code in PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#discussion_r2101962056


##########
java/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/ui/models/InlineValueComputerImpl.java:
##########
@@ -0,0 +1,475 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.debugger.jpda.ui.models;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.prefs.PreferenceChangeEvent;
+import java.util.prefs.PreferenceChangeListener;
+import java.util.prefs.Preferences;
+import java.util.stream.Collectors;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Document;
+import org.netbeans.api.debugger.DebuggerManagerAdapter;
+import org.netbeans.api.debugger.LazyDebuggerManagerListener;
+import org.netbeans.api.debugger.Session;
+import org.netbeans.api.debugger.jpda.CallStackFrame;
+import org.netbeans.api.debugger.jpda.InvalidExpressionException;
+import org.netbeans.api.debugger.jpda.JPDAClassType;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.ObjectVariable;
+import org.netbeans.api.debugger.jpda.Variable;
+import org.netbeans.api.editor.mimelookup.MimeLookup;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.editor.settings.AttributesUtilities;
+import org.netbeans.api.java.source.CancellableTask;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.JavaSource.Phase;
+import org.netbeans.api.java.source.JavaSource.Priority;
+import org.netbeans.api.java.source.JavaSourceTaskFactory;
+import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
+import org.netbeans.modules.debugger.jpda.expr.formatters.Formatters;
+import org.netbeans.modules.debugger.jpda.expr.formatters.FormattersLoopControl;
+import org.netbeans.modules.debugger.jpda.expr.formatters.VariablesFormatter;
+import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.InvalidStackFrameExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ObjectCollectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.ui.values.ComputeInlineValues;
+import org.netbeans.modules.debugger.jpda.ui.values.ComputeInlineValues.InlineVariable;
+import org.netbeans.modules.parsing.spi.TaskIndexingMode;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.debugger.ui.Constants;
+import org.netbeans.spi.editor.highlighting.HighlightsLayer;
+import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory;
+import org.netbeans.spi.editor.highlighting.ZOrder;
+import org.netbeans.spi.editor.highlighting.support.OffsetsBag;
+import org.openide.cookies.EditorCookie;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.RequestProcessor;
+import org.openide.util.WeakListeners;
+import org.openide.util.lookup.ServiceProvider;
+import org.openide.util.lookup.ServiceProviders;
+
+
+@DebuggerServiceRegistration(path="netbeans-JPDASession/inlineValue", types=InlineValueComputer.class)
+public class InlineValueComputerImpl implements InlineValueComputer, PreferenceChangeListener, PropertyChangeListener {
+
+    private static final Logger LOG = Logger.getLogger(InlineValueComputerImpl.class.getName());
+    private static final RequestProcessor EVALUATOR = new RequestProcessor(InlineValueComputerImpl.class.getName(), 1, false, false);
+    private static final String JAVA_STRATUM = "Java"; //XXX: this is probably already defined somewhere
+    private final JPDADebuggerImpl debugger;
+    private final Preferences prefs;
+    private TaskDescription currentTask;
+
+    public InlineValueComputerImpl(ContextProvider contextProvider) {
+        debugger = (JPDADebuggerImpl) contextProvider.lookupFirst(null, JPDADebugger.class);
+        debugger.addPropertyChangeListener(this);
+        prefs = MimeLookup.getLookup("text/x-java").lookup(Preferences.class);
+        prefs.addPreferenceChangeListener(WeakListeners.create(PreferenceChangeListener.class, this, prefs));
+    }
+
+    @Override
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (JPDADebugger.PROP_STATE.equals(evt.getPropertyName()) &&
+            debugger.getState() == JPDADebugger.STATE_DISCONNECTED) {
+            setNewTask(null);
+        }
+
+        if (JPDADebugger.PROP_CURRENT_CALL_STACK_FRAME.equals(evt.getPropertyName())) {
+            refreshVariables();
+        }
+    }
+
+    @Override
+    public void preferenceChange(PreferenceChangeEvent evt) {
+        refreshVariables();
+    }
+
+    private void refreshVariables() {
+        CallStackFrame frame = debugger.getCurrentCallStackFrame();
+
+        FileObject frameFile = null;
+        int frameLineNumber = -1;
+        Document frameDocument = null;
+
+        if (prefs.getBoolean(Constants.KEY_INLINE_VALUES, Constants.DEF_INLINE_VALUES) &&
+            frame != null && !frame.isObsolete() &&
+            frame.getThread().isSuspended() &&
+            JAVA_STRATUM.equals(frame.getDefaultStratum())) {
+            try {
+                String url = debugger.getEngineContext().getURL(frame, JAVA_STRATUM);
+                frameFile = url != null ? URLMapper.findFileObject(URI.create(url).toURL()) : null;
+                if (frameFile != null) {
+                    frameLineNumber = frame.getLineNumber(JAVA_STRATUM);
+                    EditorCookie ec = frameFile.getLookup().lookup(EditorCookie.class);
+                    frameDocument = ec != null ? ec.getDocument() : null;
+                }
+            } catch (InternalExceptionWrapper | InvalidStackFrameExceptionWrapper | ObjectCollectedExceptionWrapper | VMDisconnectedExceptionWrapper | MalformedURLException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        }
+
+        TaskDescription newTask;
+
+        if (frameFile != null && frameDocument != null) {
+            newTask = new TaskDescription(frameFile, frameLineNumber, frameDocument);
+        } else {
+            newTask = null;
+        }
+
+        if (setNewTask(newTask)) {
+            return;
+        }
+
+        if (newTask != null) {
+            CountDownLatch computationDone = new CountDownLatch(1);
+
+            newTask.addCancelCallback(computationDone::countDown);
+
+            AtomicReference<Collection<InlineVariable>> values = new AtomicReference<>();
+
+            EVALUATOR.post(() -> {
+                OffsetsBag runningBag = new OffsetsBag(newTask.frameDocument);
+
+                Lookup.getDefault().lookup(ComputeInlineVariablesFactory.class).set(newTask.frameFile, newTask.frameLineNumber, variables -> {
+                    values.set(variables);
+                    computationDone.countDown();
+                });
+
+                try {
+                    computationDone.await();
+                } catch (InterruptedException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+
+                if (newTask.isCancelled()) {
+                    return ;
+                }
+
+                Collection<InlineVariable> variables = values.get();
+
+                if (values == null) {
+                    return ;
+                }

Review Comment:
   Good catch, done.



##########
java/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/ui/models/InlineValueComputerImpl.java:
##########
@@ -0,0 +1,475 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.debugger.jpda.ui.models;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.prefs.PreferenceChangeEvent;
+import java.util.prefs.PreferenceChangeListener;
+import java.util.prefs.Preferences;
+import java.util.stream.Collectors;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Document;
+import org.netbeans.api.debugger.DebuggerManagerAdapter;
+import org.netbeans.api.debugger.LazyDebuggerManagerListener;
+import org.netbeans.api.debugger.Session;
+import org.netbeans.api.debugger.jpda.CallStackFrame;
+import org.netbeans.api.debugger.jpda.InvalidExpressionException;
+import org.netbeans.api.debugger.jpda.JPDAClassType;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.ObjectVariable;
+import org.netbeans.api.debugger.jpda.Variable;
+import org.netbeans.api.editor.mimelookup.MimeLookup;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.editor.settings.AttributesUtilities;
+import org.netbeans.api.java.source.CancellableTask;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.JavaSource.Phase;
+import org.netbeans.api.java.source.JavaSource.Priority;
+import org.netbeans.api.java.source.JavaSourceTaskFactory;
+import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
+import org.netbeans.modules.debugger.jpda.expr.formatters.Formatters;
+import org.netbeans.modules.debugger.jpda.expr.formatters.FormattersLoopControl;
+import org.netbeans.modules.debugger.jpda.expr.formatters.VariablesFormatter;
+import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.InvalidStackFrameExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ObjectCollectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.ui.values.ComputeInlineValues;
+import org.netbeans.modules.debugger.jpda.ui.values.ComputeInlineValues.InlineVariable;
+import org.netbeans.modules.parsing.spi.TaskIndexingMode;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.debugger.ui.Constants;
+import org.netbeans.spi.editor.highlighting.HighlightsLayer;
+import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory;
+import org.netbeans.spi.editor.highlighting.ZOrder;
+import org.netbeans.spi.editor.highlighting.support.OffsetsBag;
+import org.openide.cookies.EditorCookie;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.RequestProcessor;
+import org.openide.util.WeakListeners;
+import org.openide.util.lookup.ServiceProvider;
+import org.openide.util.lookup.ServiceProviders;
+
+
+@DebuggerServiceRegistration(path="netbeans-JPDASession/inlineValue", types=InlineValueComputer.class)
+public class InlineValueComputerImpl implements InlineValueComputer, PreferenceChangeListener, PropertyChangeListener {
+
+    private static final Logger LOG = Logger.getLogger(InlineValueComputerImpl.class.getName());
+    private static final RequestProcessor EVALUATOR = new RequestProcessor(InlineValueComputerImpl.class.getName(), 1, false, false);
+    private static final String JAVA_STRATUM = "Java"; //XXX: this is probably already defined somewhere
+    private final JPDADebuggerImpl debugger;
+    private final Preferences prefs;
+    private TaskDescription currentTask;
+
+    public InlineValueComputerImpl(ContextProvider contextProvider) {
+        debugger = (JPDADebuggerImpl) contextProvider.lookupFirst(null, JPDADebugger.class);
+        debugger.addPropertyChangeListener(this);
+        prefs = MimeLookup.getLookup("text/x-java").lookup(Preferences.class);
+        prefs.addPreferenceChangeListener(WeakListeners.create(PreferenceChangeListener.class, this, prefs));
+    }
+
+    @Override
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (JPDADebugger.PROP_STATE.equals(evt.getPropertyName()) &&
+            debugger.getState() == JPDADebugger.STATE_DISCONNECTED) {
+            setNewTask(null);
+        }
+
+        if (JPDADebugger.PROP_CURRENT_CALL_STACK_FRAME.equals(evt.getPropertyName())) {
+            refreshVariables();
+        }
+    }
+
+    @Override
+    public void preferenceChange(PreferenceChangeEvent evt) {
+        refreshVariables();
+    }
+
+    private void refreshVariables() {
+        CallStackFrame frame = debugger.getCurrentCallStackFrame();
+
+        FileObject frameFile = null;
+        int frameLineNumber = -1;
+        Document frameDocument = null;
+
+        if (prefs.getBoolean(Constants.KEY_INLINE_VALUES, Constants.DEF_INLINE_VALUES) &&
+            frame != null && !frame.isObsolete() &&
+            frame.getThread().isSuspended() &&
+            JAVA_STRATUM.equals(frame.getDefaultStratum())) {
+            try {
+                String url = debugger.getEngineContext().getURL(frame, JAVA_STRATUM);
+                frameFile = url != null ? URLMapper.findFileObject(URI.create(url).toURL()) : null;
+                if (frameFile != null) {
+                    frameLineNumber = frame.getLineNumber(JAVA_STRATUM);
+                    EditorCookie ec = frameFile.getLookup().lookup(EditorCookie.class);
+                    frameDocument = ec != null ? ec.getDocument() : null;
+                }
+            } catch (InternalExceptionWrapper | InvalidStackFrameExceptionWrapper | ObjectCollectedExceptionWrapper | VMDisconnectedExceptionWrapper | MalformedURLException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        }
+
+        TaskDescription newTask;
+
+        if (frameFile != null && frameDocument != null) {
+            newTask = new TaskDescription(frameFile, frameLineNumber, frameDocument);
+        } else {
+            newTask = null;
+        }
+
+        if (setNewTask(newTask)) {
+            return;
+        }
+
+        if (newTask != null) {
+            CountDownLatch computationDone = new CountDownLatch(1);
+
+            newTask.addCancelCallback(computationDone::countDown);
+
+            AtomicReference<Collection<InlineVariable>> values = new AtomicReference<>();
+
+            EVALUATOR.post(() -> {
+                OffsetsBag runningBag = new OffsetsBag(newTask.frameDocument);
+
+                Lookup.getDefault().lookup(ComputeInlineVariablesFactory.class).set(newTask.frameFile, newTask.frameLineNumber, variables -> {
+                    values.set(variables);
+                    computationDone.countDown();
+                });
+
+                try {
+                    computationDone.await();
+                } catch (InterruptedException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+
+                if (newTask.isCancelled()) {
+                    return ;
+                }
+
+                Collection<InlineVariable> variables = values.get();
+
+                if (values == null) {
+                    return ;
+                }
+
+                Map<String, Variable> expression2Value = new HashMap<>();
+                Map<Integer, Map<String, String>> line2Values = new HashMap<>();
+
+                for (InlineVariable v : variables) {
+                    if (newTask.isCancelled()) {
+                        return ;
+                    }
+
+                    Variable value = expression2Value.computeIfAbsent(v.expression(), expr -> {
+                        try {
+                            return debugger.evaluate(expr);
+                        } catch (InvalidExpressionException ex) {
+                            //the variable may not exist
+                            LOG.log(Level.FINE, null, ex);
+                            return null;
+                        }
+                    });
+                    if (value != null) {
+                        String valueText;
+                        if (value instanceof ObjectVariable ov) {
+                            valueText = toValue(ov).replace("\n", "\\n");
+                        } else {
+                            valueText = value.getValue();
+                        }
+                        line2Values.computeIfAbsent(v.lineEnd(), __ -> new LinkedHashMap<>())
+                                   .putIfAbsent(v.expression(), v.expression() + " = " + valueText);
+                        String mergedValues = line2Values.get(v.lineEnd()).values().stream().collect(Collectors.joining(", ", "  ", ""));
+                        AttributeSet attrs = AttributesUtilities.createImmutable("virtual-text-prepend", mergedValues);
+
+                        runningBag.addHighlight(v.lineEnd(), v.lineEnd() + 1, attrs);
+
+                        setHighlights(newTask, runningBag);
+                    }
+                }
+            });
+        }
+    }
+
+    private String toValue(ObjectVariable variable) {
+        //mostly copied from the VariablesFormatterFilter.getValueAt:
+        FormattersLoopControl formattersLoop = new FormattersLoopControl();
+        String type = variable.getType ();
+        ObjectVariable ov = (ObjectVariable) variable;
+        JPDAClassType ct = ov.getClassType();
+        if (ct == null) {
+            return ov.getValue();
+        }
+        VariablesFormatter f = Formatters.getFormatterForType(ct, formattersLoop.getFormatters());
+        String[] formattersInLoopRef = new String[] { null };
+        if (f != null && formattersLoop.canUse(f, ct.getName(), formattersInLoopRef)) {
+            String code = f.getValueFormatCode();
+            if (code != null && code.length() > 0) {
+                try {
+                    java.lang.reflect.Method evaluateMethod = ov.getClass().getMethod("evaluate", String.class);
+                    evaluateMethod.setAccessible(true);
+                    Variable ret = (Variable) evaluateMethod.invoke(ov, code);
+                    if (ret == null) {
+                        return null;
+                    }
+                    return ret.getValue();
+                } catch (java.lang.reflect.InvocationTargetException itex) {
+                    Throwable t = itex.getTargetException();
+                    if (t instanceof InvalidExpressionException) {
+                        return ov.getValue();
+                    } else {
+                        Exceptions.printStackTrace(t);
+                    }
+                } catch (NoSuchMethodException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (SecurityException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (IllegalAccessException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (IllegalArgumentException ex) {

Review Comment:
   Done.



##########
java/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/ui/models/InlineValueComputerImpl.java:
##########
@@ -0,0 +1,475 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.debugger.jpda.ui.models;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.prefs.PreferenceChangeEvent;
+import java.util.prefs.PreferenceChangeListener;
+import java.util.prefs.Preferences;
+import java.util.stream.Collectors;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Document;
+import org.netbeans.api.debugger.DebuggerManagerAdapter;
+import org.netbeans.api.debugger.LazyDebuggerManagerListener;
+import org.netbeans.api.debugger.Session;
+import org.netbeans.api.debugger.jpda.CallStackFrame;
+import org.netbeans.api.debugger.jpda.InvalidExpressionException;
+import org.netbeans.api.debugger.jpda.JPDAClassType;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.ObjectVariable;
+import org.netbeans.api.debugger.jpda.Variable;
+import org.netbeans.api.editor.mimelookup.MimeLookup;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.editor.settings.AttributesUtilities;
+import org.netbeans.api.java.source.CancellableTask;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.JavaSource.Phase;
+import org.netbeans.api.java.source.JavaSource.Priority;
+import org.netbeans.api.java.source.JavaSourceTaskFactory;
+import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
+import org.netbeans.modules.debugger.jpda.expr.formatters.Formatters;
+import org.netbeans.modules.debugger.jpda.expr.formatters.FormattersLoopControl;
+import org.netbeans.modules.debugger.jpda.expr.formatters.VariablesFormatter;
+import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.InvalidStackFrameExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ObjectCollectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.ui.values.ComputeInlineValues;
+import org.netbeans.modules.debugger.jpda.ui.values.ComputeInlineValues.InlineVariable;
+import org.netbeans.modules.parsing.spi.TaskIndexingMode;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.debugger.ui.Constants;
+import org.netbeans.spi.editor.highlighting.HighlightsLayer;
+import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory;
+import org.netbeans.spi.editor.highlighting.ZOrder;
+import org.netbeans.spi.editor.highlighting.support.OffsetsBag;
+import org.openide.cookies.EditorCookie;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.RequestProcessor;
+import org.openide.util.WeakListeners;
+import org.openide.util.lookup.ServiceProvider;
+import org.openide.util.lookup.ServiceProviders;
+
+
+@DebuggerServiceRegistration(path="netbeans-JPDASession/inlineValue", types=InlineValueComputer.class)
+public class InlineValueComputerImpl implements InlineValueComputer, PreferenceChangeListener, PropertyChangeListener {
+
+    private static final Logger LOG = Logger.getLogger(InlineValueComputerImpl.class.getName());
+    private static final RequestProcessor EVALUATOR = new RequestProcessor(InlineValueComputerImpl.class.getName(), 1, false, false);
+    private static final String JAVA_STRATUM = "Java"; //XXX: this is probably already defined somewhere
+    private final JPDADebuggerImpl debugger;
+    private final Preferences prefs;
+    private TaskDescription currentTask;
+
+    public InlineValueComputerImpl(ContextProvider contextProvider) {
+        debugger = (JPDADebuggerImpl) contextProvider.lookupFirst(null, JPDADebugger.class);
+        debugger.addPropertyChangeListener(this);
+        prefs = MimeLookup.getLookup("text/x-java").lookup(Preferences.class);
+        prefs.addPreferenceChangeListener(WeakListeners.create(PreferenceChangeListener.class, this, prefs));
+    }
+
+    @Override
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (JPDADebugger.PROP_STATE.equals(evt.getPropertyName()) &&
+            debugger.getState() == JPDADebugger.STATE_DISCONNECTED) {
+            setNewTask(null);
+        }
+
+        if (JPDADebugger.PROP_CURRENT_CALL_STACK_FRAME.equals(evt.getPropertyName())) {
+            refreshVariables();
+        }
+    }
+
+    @Override
+    public void preferenceChange(PreferenceChangeEvent evt) {
+        refreshVariables();
+    }
+
+    private void refreshVariables() {
+        CallStackFrame frame = debugger.getCurrentCallStackFrame();
+
+        FileObject frameFile = null;
+        int frameLineNumber = -1;
+        Document frameDocument = null;
+
+        if (prefs.getBoolean(Constants.KEY_INLINE_VALUES, Constants.DEF_INLINE_VALUES) &&
+            frame != null && !frame.isObsolete() &&
+            frame.getThread().isSuspended() &&
+            JAVA_STRATUM.equals(frame.getDefaultStratum())) {
+            try {
+                String url = debugger.getEngineContext().getURL(frame, JAVA_STRATUM);
+                frameFile = url != null ? URLMapper.findFileObject(URI.create(url).toURL()) : null;
+                if (frameFile != null) {
+                    frameLineNumber = frame.getLineNumber(JAVA_STRATUM);
+                    EditorCookie ec = frameFile.getLookup().lookup(EditorCookie.class);
+                    frameDocument = ec != null ? ec.getDocument() : null;
+                }
+            } catch (InternalExceptionWrapper | InvalidStackFrameExceptionWrapper | ObjectCollectedExceptionWrapper | VMDisconnectedExceptionWrapper | MalformedURLException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        }
+
+        TaskDescription newTask;
+
+        if (frameFile != null && frameDocument != null) {
+            newTask = new TaskDescription(frameFile, frameLineNumber, frameDocument);
+        } else {
+            newTask = null;
+        }
+
+        if (setNewTask(newTask)) {
+            return;
+        }
+
+        if (newTask != null) {
+            CountDownLatch computationDone = new CountDownLatch(1);
+
+            newTask.addCancelCallback(computationDone::countDown);
+
+            AtomicReference<Collection<InlineVariable>> values = new AtomicReference<>();
+
+            EVALUATOR.post(() -> {
+                OffsetsBag runningBag = new OffsetsBag(newTask.frameDocument);
+
+                Lookup.getDefault().lookup(ComputeInlineVariablesFactory.class).set(newTask.frameFile, newTask.frameLineNumber, variables -> {
+                    values.set(variables);
+                    computationDone.countDown();
+                });
+
+                try {
+                    computationDone.await();
+                } catch (InterruptedException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+
+                if (newTask.isCancelled()) {
+                    return ;
+                }
+
+                Collection<InlineVariable> variables = values.get();
+
+                if (values == null) {
+                    return ;
+                }
+
+                Map<String, Variable> expression2Value = new HashMap<>();
+                Map<Integer, Map<String, String>> line2Values = new HashMap<>();
+
+                for (InlineVariable v : variables) {
+                    if (newTask.isCancelled()) {
+                        return ;
+                    }
+
+                    Variable value = expression2Value.computeIfAbsent(v.expression(), expr -> {
+                        try {
+                            return debugger.evaluate(expr);
+                        } catch (InvalidExpressionException ex) {
+                            //the variable may not exist
+                            LOG.log(Level.FINE, null, ex);
+                            return null;
+                        }
+                    });
+                    if (value != null) {
+                        String valueText;
+                        if (value instanceof ObjectVariable ov) {
+                            valueText = toValue(ov).replace("\n", "\\n");
+                        } else {
+                            valueText = value.getValue();
+                        }
+                        line2Values.computeIfAbsent(v.lineEnd(), __ -> new LinkedHashMap<>())
+                                   .putIfAbsent(v.expression(), v.expression() + " = " + valueText);
+                        String mergedValues = line2Values.get(v.lineEnd()).values().stream().collect(Collectors.joining(", ", "  ", ""));
+                        AttributeSet attrs = AttributesUtilities.createImmutable("virtual-text-prepend", mergedValues);
+
+                        runningBag.addHighlight(v.lineEnd(), v.lineEnd() + 1, attrs);
+
+                        setHighlights(newTask, runningBag);
+                    }
+                }
+            });
+        }
+    }
+
+    private String toValue(ObjectVariable variable) {
+        //mostly copied from the VariablesFormatterFilter.getValueAt:
+        FormattersLoopControl formattersLoop = new FormattersLoopControl();
+        String type = variable.getType ();
+        ObjectVariable ov = (ObjectVariable) variable;
+        JPDAClassType ct = ov.getClassType();
+        if (ct == null) {
+            return ov.getValue();
+        }
+        VariablesFormatter f = Formatters.getFormatterForType(ct, formattersLoop.getFormatters());
+        String[] formattersInLoopRef = new String[] { null };
+        if (f != null && formattersLoop.canUse(f, ct.getName(), formattersInLoopRef)) {
+            String code = f.getValueFormatCode();
+            if (code != null && code.length() > 0) {
+                try {
+                    java.lang.reflect.Method evaluateMethod = ov.getClass().getMethod("evaluate", String.class);
+                    evaluateMethod.setAccessible(true);
+                    Variable ret = (Variable) evaluateMethod.invoke(ov, code);
+                    if (ret == null) {
+                        return null;
+                    }
+                    return ret.getValue();
+                } catch (java.lang.reflect.InvocationTargetException itex) {
+                    Throwable t = itex.getTargetException();
+                    if (t instanceof InvalidExpressionException) {
+                        return ov.getValue();
+                    } else {
+                        Exceptions.printStackTrace(t);
+                    }
+                } catch (NoSuchMethodException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (SecurityException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (IllegalAccessException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (IllegalArgumentException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+            }
+        } else if (formattersInLoopRef[0] != null) {
+            //ignore loops(?)
+        }
+        if (VariablesFormatterFilter.isToStringValueType(type)) {
+            try {
+                return "\""+ov.getToStringValue ()+"\"";
+            } catch (InvalidExpressionException ex) {
+                // Not a supported operation (e.g. J2ME, see #45543)
+                // Or missing context or any other reason
+                Logger.getLogger(VariablesFormatterFilter.class.getName()).fine("getToStringValue() "+ex.getLocalizedMessage());
+                if ( (ex.getTargetException () != null) &&
+                     (ex.getTargetException () instanceof

Review Comment:
   Done.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Inlining values inside debugger [netbeans]

Posted by "mbien (via GitHub)" <gi...@apache.org>.
mbien commented on code in PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#discussion_r2078918705


##########
java/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/ui/values/ComputeInlineValues.java:
##########
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.debugger.jpda.ui.values;
+
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.LambdaExpressionTree;
+import com.sun.source.tree.LineMap;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreePath;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.TreeUtilities;
+import org.netbeans.api.java.source.support.CancellableTreePathScanner;
+
+public class ComputeInlineValues {
+
+    public static Collection<InlineVariable> computeVariables(CompilationInfo info, int stackLine, int stackCol, AtomicBoolean cancel) {
+        Collection<InlineVariable> result = new ArrayList<>();
+        int donePos = (int) info.getCompilationUnit().getLineMap().getPosition(stackLine, stackCol);
+        int upcomingPos = (int) info.getCompilationUnit().getLineMap().getStartPosition(stackLine + 1);
+        TreePath relevantPoint = info.getTreeUtilities().pathFor(donePos);
+        OUTER: while (relevantPoint != null) {
+            Tree leaf = relevantPoint.getLeaf();
+            switch (leaf.getKind()) {
+                case METHOD: case LAMBDA_EXPRESSION: break OUTER;
+                case BLOCK:
+                    if (relevantPoint.getParentPath() != null && TreeUtilities.CLASS_TREE_KINDS.contains(relevantPoint.getParentPath().getLeaf().getKind())) {
+                        break OUTER;
+                    }
+            }
+            relevantPoint = relevantPoint.getParentPath();
+        }
+        LineMap lm = info.getCompilationUnit().getLineMap();
+        new CancellableTreePathScanner<Void, Void>(cancel) {
+            @Override
+            public Void visitVariable(VariableTree node, Void p) {
+                int end = (int) info.getTrees().getSourcePositions().getEndPosition(info.getCompilationUnit(), node);
+                if (end < donePos) {
+                    int[] span = info.getTreeUtilities().findNameSpan(node);
+
+                    if (span != null) {
+                        int lineEnd = (int) (lm.getStartPosition(lm.getLineNumber(span[1]) + 1) - 1);
+
+                        result.add(new InlineVariable(span[0], span[1], lineEnd, node.getName().toString()));
+                    }
+                }
+                return super.visitVariable(node, p);
+            }
+
+            @Override
+            public Void visitIdentifier(IdentifierTree node, Void p) {
+                Element el = info.getTrees().getElement(getCurrentPath());
+
+                if (el != null && el.getKind().isVariable() &&
+                    el.getKind() != ElementKind.ENUM_CONSTANT) {
+                    int start = (int) info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), node);
+                    int end = (int) info.getTrees().getSourcePositions().getEndPosition(info.getCompilationUnit(), node);
+
+                    if (start != (-1) && end != (-1)) {
+                        int lineEnd = (int) (lm.getStartPosition(lm.getLineNumber(end) + 1) - 1);
+
+                        result.add(new InlineVariable(start, end, lineEnd, node.getName().toString()));
+                    }
+                }
+
+                return super.visitIdentifier(node, p);
+            }
+
+            @Override
+            public Void visitClass(ClassTree node, Void p) {
+                return null;
+            }
+
+            @Override
+            public Void visitLambdaExpression(LambdaExpressionTree node, Void p) {
+                return null;
+            }
+
+            @Override
+            public Void scan(Tree tree, Void p) {
+                if (tree != null) {
+                    int start = (int) info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), tree);
+
+                    if (start > upcomingPos) {
+                        return null;
+                    }
+                }
+                return super.scan(tree, p);
+            }
+
+        }.scan(relevantPoint, null);
+
+        return result;
+    }
+
+    public static final class InlineVariable {

Review Comment:
   record candidate



##########
ide/spi.debugger.ui/apichanges.xml:
##########
@@ -51,6 +51,23 @@
 <!-- ACTUAL CHANGES BEGIN HERE: -->
 
 <changes>
+    <change id="inline-values-setting-keys">
+        <api name="DebuggerCoreSPI"/>
+        <summary><code>Constants</code> class enhanced with constants for inline values.</summary>
+        <version major="2" minor="88"/>
+        <date day="8" month="4" year="2025"/>
+        <author login="jlahoda"/>
+        <compatibility binary="compatible" source="compatible" modification="yes" semantic="compatible"/>
+        <description>
+            <p>
+                Two new contants are added to <code>Constants</code>:

Review Comment:
   contants -> constants



##########
ide/api.lsp/src/org/netbeans/api/lsp/InlineValue.java:
##########
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.api.lsp;
+
+/**
+ * An expression whose value may be show inline while debugging.
+ *
+ * @since 1.35
+ */
+public final class InlineValue {

Review Comment:
   record candidate



##########
ide/api.lsp/src/org/netbeans/api/lsp/InlineValue.java:
##########
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.api.lsp;
+
+/**
+ * An expression whose value may be show inline while debugging.

Review Comment:
   show -> shown



##########
java/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/ui/models/InlineValueComputerImpl.java:
##########
@@ -0,0 +1,477 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.debugger.jpda.ui.models;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.prefs.PreferenceChangeEvent;
+import java.util.prefs.PreferenceChangeListener;
+import java.util.prefs.Preferences;
+import java.util.stream.Collectors;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Document;
+import org.netbeans.api.debugger.DebuggerManagerAdapter;
+import org.netbeans.api.debugger.LazyDebuggerManagerListener;
+import org.netbeans.api.debugger.Session;
+import org.netbeans.api.debugger.jpda.CallStackFrame;
+import org.netbeans.api.debugger.jpda.InvalidExpressionException;
+import org.netbeans.api.debugger.jpda.JPDAClassType;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.ObjectVariable;
+import org.netbeans.api.debugger.jpda.Variable;
+import org.netbeans.api.editor.mimelookup.MimeLookup;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.editor.settings.AttributesUtilities;
+import org.netbeans.api.java.source.CancellableTask;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.JavaSource.Phase;
+import org.netbeans.api.java.source.JavaSource.Priority;
+import org.netbeans.api.java.source.JavaSourceTaskFactory;
+import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
+import org.netbeans.modules.debugger.jpda.expr.formatters.Formatters;
+import org.netbeans.modules.debugger.jpda.expr.formatters.FormattersLoopControl;
+import org.netbeans.modules.debugger.jpda.expr.formatters.VariablesFormatter;
+import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.InvalidStackFrameExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ObjectCollectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.ui.values.ComputeInlineValues;
+import org.netbeans.modules.debugger.jpda.ui.values.ComputeInlineValues.InlineVariable;
+import org.netbeans.modules.parsing.spi.TaskIndexingMode;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.debugger.ui.Constants;
+import org.netbeans.spi.editor.highlighting.HighlightsLayer;
+import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory;
+import org.netbeans.spi.editor.highlighting.HighlightsSequence;
+import org.netbeans.spi.editor.highlighting.ZOrder;
+import org.netbeans.spi.editor.highlighting.support.OffsetsBag;
+import org.openide.cookies.EditorCookie;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.RequestProcessor;
+import org.openide.util.WeakListeners;
+import org.openide.util.lookup.ServiceProvider;
+import org.openide.util.lookup.ServiceProviders;
+
+
+@DebuggerServiceRegistration(path="netbeans-JPDASession/inlineValue", types=InlineValueComputer.class)
+public class InlineValueComputerImpl implements InlineValueComputer, PreferenceChangeListener, PropertyChangeListener {
+
+    private static final Logger LOG = Logger.getLogger(InlineValueComputerImpl.class.getName());
+    private static final RequestProcessor EVALUATOR = new RequestProcessor(InlineValueComputerImpl.class.getName(), 1, false, false);
+    private static final String JAVA_STRATUM = "Java"; //XXX: this is probably already defined somewhere
+    private final JPDADebuggerImpl debugger;
+    private final Preferences prefs;
+    private TaskDescription currentTask;
+
+    public InlineValueComputerImpl(ContextProvider contextProvider) {
+        debugger = (JPDADebuggerImpl) contextProvider.lookupFirst(null, JPDADebugger.class);
+        debugger.addPropertyChangeListener(this);
+        prefs = MimeLookup.getLookup("text/x-java").lookup(Preferences.class);
+        prefs.addPreferenceChangeListener(WeakListeners.create(PreferenceChangeListener.class, this, prefs));
+    }
+
+    @Override
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (JPDADebugger.PROP_STATE.equals(evt.getPropertyName()) &&
+            debugger.getState() == JPDADebugger.STATE_DISCONNECTED) {
+            setNewTask(null);
+        }
+
+        if (JPDADebugger.PROP_CURRENT_CALL_STACK_FRAME.equals(evt.getPropertyName())) {
+            refreshVariables();
+        }
+    }
+
+    @Override
+    public void preferenceChange(PreferenceChangeEvent evt) {
+        refreshVariables();
+    }
+
+    private void refreshVariables() {
+        CallStackFrame frame = debugger.getCurrentCallStackFrame();
+
+        FileObject frameFile = null;
+        int frameLineNumber = -1;
+        Document frameDocument = null;
+
+        if (prefs.getBoolean(Constants.KEY_INLINE_VALUES, Constants.DEF_INLINE_VALUES) &&
+            frame != null && !frame.isObsolete() &&
+            frame.getThread().isSuspended() &&
+            JAVA_STRATUM.equals(frame.getDefaultStratum())) {
+            try {
+                String url = debugger.getEngineContext().getURL(frame, JAVA_STRATUM);
+                frameFile = url != null ? URLMapper.findFileObject(URI.create(url).toURL()) : null;
+                if (frameFile != null) {
+                    frameLineNumber = frame.getLineNumber(JAVA_STRATUM);
+                    EditorCookie ec = frameFile.getLookup().lookup(EditorCookie.class);
+                    frameDocument = ec != null ? ec.getDocument() : null;
+                }
+            } catch (InternalExceptionWrapper | InvalidStackFrameExceptionWrapper | ObjectCollectedExceptionWrapper | VMDisconnectedExceptionWrapper | MalformedURLException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        }
+
+        TaskDescription newTask;
+
+        if (frameFile != null && frameDocument != null) {
+            newTask = new TaskDescription(frameFile, frameLineNumber, frameDocument);
+        } else {
+            newTask = null;
+        }
+
+        if (setNewTask(newTask)) {
+            return;
+        }
+
+        if (newTask != null) {
+            //TODO: cancel any already running computation if the configuration is different:
+            CountDownLatch computationDone = new CountDownLatch(1);
+
+            newTask.addCancelCallback(computationDone::countDown);
+
+            AtomicReference<Collection<InlineVariable>> values = new AtomicReference<>();
+
+            EVALUATOR.post(() -> {
+                OffsetsBag runningBag = new OffsetsBag(newTask.frameDocument);
+
+                Lookup.getDefault().lookup(ComputeInlineVariablesFactory.class).set(newTask.frameFile, newTask.frameLineNumber, variables -> {
+                    values.set(variables);
+                    computationDone.countDown();
+                });
+
+                try {
+                    computationDone.await();
+                } catch (InterruptedException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+
+                if (newTask.isCancelled()) {
+                    return ;
+                }
+
+                Collection<InlineVariable> variables = values.get();
+
+                if (values == null) {
+                    return ;
+                }
+
+                Map<String, Variable> expression2Value = new HashMap<>();
+                Map<Integer, Map<String, String>> line2Values = new HashMap<>();
+
+                for (InlineVariable v : variables) {
+                    if (newTask.isCancelled()) {
+                        return ;
+                    }
+
+                    Variable value = expression2Value.computeIfAbsent(v.expression, expr -> {
+                        try {
+                            return debugger.evaluate(expr);
+                        } catch (InvalidExpressionException ex) {
+                            //the variable may not exist
+                            LOG.log(Level.FINE, null, ex);
+                            return null;
+                        }
+                    });
+                    if (value != null) {
+                        String valueText;
+                        if (value instanceof ObjectVariable ov) {
+                            valueText = toValue(ov).replace("\n", "\\n");
+                        } else {
+                            valueText = value.getValue();
+                        }
+                        line2Values.computeIfAbsent(v.lineEnd, __ -> new LinkedHashMap<>())
+                                   .putIfAbsent(v.expression, v.expression + " = " + valueText);
+                        String mergedValues = line2Values.get(v.lineEnd).values().stream().collect(Collectors.joining(", ", "  ", ""));
+                        AttributeSet attrs = AttributesUtilities.createImmutable("virtual-text-prepend", mergedValues);
+
+                        runningBag.addHighlight(v.lineEnd, v.lineEnd + 1, attrs);
+
+                        setHighlights(newTask, runningBag);
+                    }
+                }
+            });
+        }
+    }
+
+    private String toValue(ObjectVariable variable) {
+        //mostly copied from the VariablesFormatterFilter.getValueAt:
+        FormattersLoopControl formattersLoop = new FormattersLoopControl();
+        String type = variable.getType ();
+        ObjectVariable ov = (ObjectVariable) variable;
+        JPDAClassType ct = ov.getClassType();
+        if (ct == null) {
+            return ov.getValue();
+        }
+        VariablesFormatter f = Formatters.getFormatterForType(ct, formattersLoop.getFormatters());
+        String[] formattersInLoopRef = new String[] { null };
+        if (f != null && formattersLoop.canUse(f, ct.getName(), formattersInLoopRef)) {
+            String code = f.getValueFormatCode();
+            if (code != null && code.length() > 0) {
+                try {
+                    java.lang.reflect.Method evaluateMethod = ov.getClass().getMethod("evaluate", String.class);
+                    evaluateMethod.setAccessible(true);
+                    Variable ret = (Variable) evaluateMethod.invoke(ov, code);
+                    if (ret == null) {
+                        return null;
+                    }
+                    return ret.getValue();
+                } catch (java.lang.reflect.InvocationTargetException itex) {
+                    Throwable t = itex.getTargetException();
+                    if (t instanceof InvalidExpressionException) {
+                        return ov.getValue();
+                    } else {
+                        Exceptions.printStackTrace(t);
+                    }
+                } catch (NoSuchMethodException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (SecurityException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (IllegalAccessException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (IllegalArgumentException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+            }
+        } else if (formattersInLoopRef[0] != null) {
+            //ignore loops(?)
+        }
+        if (VariablesFormatterFilter.isToStringValueType(type)) {
+            try {
+                return "\""+ov.getToStringValue ()+"\"";
+            } catch (InvalidExpressionException ex) {
+                // Not a supported operation (e.g. J2ME, see #45543)
+                // Or missing context or any other reason
+                Logger.getLogger(VariablesFormatterFilter.class.getName()).fine("getToStringValue() "+ex.getLocalizedMessage());
+                if ( (ex.getTargetException () != null) &&
+                     (ex.getTargetException () instanceof
+                       UnsupportedOperationException)
+                ) {
+                    // PATCH for J2ME. see 45543
+                    return ov.getValue();
+                }
+                return ex.getLocalizedMessage ();
+            }
+        }
+        return ov.getValue();
+    }
+
+    private synchronized boolean setNewTask(TaskDescription newTask) {
+        if (Objects.equals(currentTask, newTask)) {
+            return true; //nothing changed, nothing to do
+        }
+
+        if (currentTask != null) {
+            currentTask.cancel();
+            getHighlightsBag(currentTask.frameDocument).clear();
+        }
+
+        currentTask = newTask;
+
+        return false;
+    }
+
+    private synchronized void setHighlights(TaskDescription task, OffsetsBag highlights) {
+        if (!task.isCancelled()) {
+            getHighlightsBag(currentTask.frameDocument).setHighlights(highlights);
+        }
+    }
+
+    @DebuggerServiceRegistration(types=LazyDebuggerManagerListener.class)
+    public static final class Init extends DebuggerManagerAdapter {
+        @Override
+        public void sessionAdded(Session session) {
+            for (InlineValueComputer v : session.lookup("inlineValue", InlineValueComputer.class)) {
+            }

Review Comment:
   todo?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Prototype for inlining values inside debugger [netbeans]

Posted by "mbien (via GitHub)" <gi...@apache.org>.
mbien commented on PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#issuecomment-2556006515

   really useful feature! We have to watch out for side effects of calling `toString()` though.
   
   The debugger does have "Variable Formatters" which can be configured in the settings. Collections (which can be large) will only print their size by default for example.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Prototype for inlining values inside debugger [netbeans]

Posted by "lahodaj (via GitHub)" <gi...@apache.org>.
lahodaj commented on PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#issuecomment-2727565143

   > really useful feature! We have to watch out for side effects of calling `toString()` though.
   > 
   > The debugger does have "Variable Formatters" which can be configured in the settings. Collections (which can be large) will only print their size by default for example. (but I imagine you are probably planning to do that anyway)
   
   Done.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Inlining values inside debugger [netbeans]

Posted by "mbien (via GitHub)" <gi...@apache.org>.
mbien commented on code in PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#discussion_r2100693402


##########
java/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/ui/models/InlineValueComputerImpl.java:
##########
@@ -0,0 +1,475 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.debugger.jpda.ui.models;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.prefs.PreferenceChangeEvent;
+import java.util.prefs.PreferenceChangeListener;
+import java.util.prefs.Preferences;
+import java.util.stream.Collectors;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Document;
+import org.netbeans.api.debugger.DebuggerManagerAdapter;
+import org.netbeans.api.debugger.LazyDebuggerManagerListener;
+import org.netbeans.api.debugger.Session;
+import org.netbeans.api.debugger.jpda.CallStackFrame;
+import org.netbeans.api.debugger.jpda.InvalidExpressionException;
+import org.netbeans.api.debugger.jpda.JPDAClassType;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.ObjectVariable;
+import org.netbeans.api.debugger.jpda.Variable;
+import org.netbeans.api.editor.mimelookup.MimeLookup;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.editor.settings.AttributesUtilities;
+import org.netbeans.api.java.source.CancellableTask;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.JavaSource.Phase;
+import org.netbeans.api.java.source.JavaSource.Priority;
+import org.netbeans.api.java.source.JavaSourceTaskFactory;
+import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
+import org.netbeans.modules.debugger.jpda.expr.formatters.Formatters;
+import org.netbeans.modules.debugger.jpda.expr.formatters.FormattersLoopControl;
+import org.netbeans.modules.debugger.jpda.expr.formatters.VariablesFormatter;
+import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.InvalidStackFrameExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ObjectCollectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.ui.values.ComputeInlineValues;
+import org.netbeans.modules.debugger.jpda.ui.values.ComputeInlineValues.InlineVariable;
+import org.netbeans.modules.parsing.spi.TaskIndexingMode;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.debugger.ui.Constants;
+import org.netbeans.spi.editor.highlighting.HighlightsLayer;
+import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory;
+import org.netbeans.spi.editor.highlighting.ZOrder;
+import org.netbeans.spi.editor.highlighting.support.OffsetsBag;
+import org.openide.cookies.EditorCookie;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.RequestProcessor;
+import org.openide.util.WeakListeners;
+import org.openide.util.lookup.ServiceProvider;
+import org.openide.util.lookup.ServiceProviders;
+
+
+@DebuggerServiceRegistration(path="netbeans-JPDASession/inlineValue", types=InlineValueComputer.class)
+public class InlineValueComputerImpl implements InlineValueComputer, PreferenceChangeListener, PropertyChangeListener {
+
+    private static final Logger LOG = Logger.getLogger(InlineValueComputerImpl.class.getName());
+    private static final RequestProcessor EVALUATOR = new RequestProcessor(InlineValueComputerImpl.class.getName(), 1, false, false);
+    private static final String JAVA_STRATUM = "Java"; //XXX: this is probably already defined somewhere
+    private final JPDADebuggerImpl debugger;
+    private final Preferences prefs;
+    private TaskDescription currentTask;
+
+    public InlineValueComputerImpl(ContextProvider contextProvider) {
+        debugger = (JPDADebuggerImpl) contextProvider.lookupFirst(null, JPDADebugger.class);
+        debugger.addPropertyChangeListener(this);
+        prefs = MimeLookup.getLookup("text/x-java").lookup(Preferences.class);
+        prefs.addPreferenceChangeListener(WeakListeners.create(PreferenceChangeListener.class, this, prefs));
+    }
+
+    @Override
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (JPDADebugger.PROP_STATE.equals(evt.getPropertyName()) &&
+            debugger.getState() == JPDADebugger.STATE_DISCONNECTED) {
+            setNewTask(null);
+        }
+
+        if (JPDADebugger.PROP_CURRENT_CALL_STACK_FRAME.equals(evt.getPropertyName())) {
+            refreshVariables();
+        }
+    }
+
+    @Override
+    public void preferenceChange(PreferenceChangeEvent evt) {
+        refreshVariables();
+    }
+
+    private void refreshVariables() {
+        CallStackFrame frame = debugger.getCurrentCallStackFrame();
+
+        FileObject frameFile = null;
+        int frameLineNumber = -1;
+        Document frameDocument = null;
+
+        if (prefs.getBoolean(Constants.KEY_INLINE_VALUES, Constants.DEF_INLINE_VALUES) &&
+            frame != null && !frame.isObsolete() &&
+            frame.getThread().isSuspended() &&
+            JAVA_STRATUM.equals(frame.getDefaultStratum())) {
+            try {
+                String url = debugger.getEngineContext().getURL(frame, JAVA_STRATUM);
+                frameFile = url != null ? URLMapper.findFileObject(URI.create(url).toURL()) : null;
+                if (frameFile != null) {
+                    frameLineNumber = frame.getLineNumber(JAVA_STRATUM);
+                    EditorCookie ec = frameFile.getLookup().lookup(EditorCookie.class);
+                    frameDocument = ec != null ? ec.getDocument() : null;
+                }
+            } catch (InternalExceptionWrapper | InvalidStackFrameExceptionWrapper | ObjectCollectedExceptionWrapper | VMDisconnectedExceptionWrapper | MalformedURLException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        }
+
+        TaskDescription newTask;
+
+        if (frameFile != null && frameDocument != null) {
+            newTask = new TaskDescription(frameFile, frameLineNumber, frameDocument);
+        } else {
+            newTask = null;
+        }
+
+        if (setNewTask(newTask)) {
+            return;
+        }
+
+        if (newTask != null) {
+            CountDownLatch computationDone = new CountDownLatch(1);
+
+            newTask.addCancelCallback(computationDone::countDown);
+
+            AtomicReference<Collection<InlineVariable>> values = new AtomicReference<>();
+
+            EVALUATOR.post(() -> {
+                OffsetsBag runningBag = new OffsetsBag(newTask.frameDocument);
+
+                Lookup.getDefault().lookup(ComputeInlineVariablesFactory.class).set(newTask.frameFile, newTask.frameLineNumber, variables -> {
+                    values.set(variables);
+                    computationDone.countDown();
+                });
+
+                try {
+                    computationDone.await();
+                } catch (InterruptedException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+
+                if (newTask.isCancelled()) {
+                    return ;
+                }
+
+                Collection<InlineVariable> variables = values.get();
+
+                if (values == null) {
+                    return ;
+                }
+
+                Map<String, Variable> expression2Value = new HashMap<>();
+                Map<Integer, Map<String, String>> line2Values = new HashMap<>();
+
+                for (InlineVariable v : variables) {
+                    if (newTask.isCancelled()) {
+                        return ;
+                    }
+
+                    Variable value = expression2Value.computeIfAbsent(v.expression(), expr -> {
+                        try {
+                            return debugger.evaluate(expr);
+                        } catch (InvalidExpressionException ex) {
+                            //the variable may not exist
+                            LOG.log(Level.FINE, null, ex);
+                            return null;
+                        }
+                    });
+                    if (value != null) {
+                        String valueText;
+                        if (value instanceof ObjectVariable ov) {
+                            valueText = toValue(ov).replace("\n", "\\n");
+                        } else {
+                            valueText = value.getValue();
+                        }
+                        line2Values.computeIfAbsent(v.lineEnd(), __ -> new LinkedHashMap<>())
+                                   .putIfAbsent(v.expression(), v.expression() + " = " + valueText);
+                        String mergedValues = line2Values.get(v.lineEnd()).values().stream().collect(Collectors.joining(", ", "  ", ""));
+                        AttributeSet attrs = AttributesUtilities.createImmutable("virtual-text-prepend", mergedValues);
+
+                        runningBag.addHighlight(v.lineEnd(), v.lineEnd() + 1, attrs);
+
+                        setHighlights(newTask, runningBag);
+                    }
+                }
+            });
+        }
+    }
+
+    private String toValue(ObjectVariable variable) {
+        //mostly copied from the VariablesFormatterFilter.getValueAt:
+        FormattersLoopControl formattersLoop = new FormattersLoopControl();
+        String type = variable.getType ();
+        ObjectVariable ov = (ObjectVariable) variable;
+        JPDAClassType ct = ov.getClassType();
+        if (ct == null) {
+            return ov.getValue();
+        }
+        VariablesFormatter f = Formatters.getFormatterForType(ct, formattersLoop.getFormatters());
+        String[] formattersInLoopRef = new String[] { null };
+        if (f != null && formattersLoop.canUse(f, ct.getName(), formattersInLoopRef)) {
+            String code = f.getValueFormatCode();
+            if (code != null && code.length() > 0) {
+                try {
+                    java.lang.reflect.Method evaluateMethod = ov.getClass().getMethod("evaluate", String.class);
+                    evaluateMethod.setAccessible(true);
+                    Variable ret = (Variable) evaluateMethod.invoke(ov, code);
+                    if (ret == null) {
+                        return null;
+                    }
+                    return ret.getValue();
+                } catch (java.lang.reflect.InvocationTargetException itex) {
+                    Throwable t = itex.getTargetException();
+                    if (t instanceof InvalidExpressionException) {
+                        return ov.getValue();
+                    } else {
+                        Exceptions.printStackTrace(t);
+                    }
+                } catch (NoSuchMethodException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (SecurityException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (IllegalAccessException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (IllegalArgumentException ex) {

Review Comment:
   -> `} catch (ReflectiveOperationException ex) {`



##########
java/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/ui/models/InlineValueComputerImpl.java:
##########
@@ -0,0 +1,475 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.debugger.jpda.ui.models;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.prefs.PreferenceChangeEvent;
+import java.util.prefs.PreferenceChangeListener;
+import java.util.prefs.Preferences;
+import java.util.stream.Collectors;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Document;
+import org.netbeans.api.debugger.DebuggerManagerAdapter;
+import org.netbeans.api.debugger.LazyDebuggerManagerListener;
+import org.netbeans.api.debugger.Session;
+import org.netbeans.api.debugger.jpda.CallStackFrame;
+import org.netbeans.api.debugger.jpda.InvalidExpressionException;
+import org.netbeans.api.debugger.jpda.JPDAClassType;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.ObjectVariable;
+import org.netbeans.api.debugger.jpda.Variable;
+import org.netbeans.api.editor.mimelookup.MimeLookup;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.editor.settings.AttributesUtilities;
+import org.netbeans.api.java.source.CancellableTask;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.JavaSource.Phase;
+import org.netbeans.api.java.source.JavaSource.Priority;
+import org.netbeans.api.java.source.JavaSourceTaskFactory;
+import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
+import org.netbeans.modules.debugger.jpda.expr.formatters.Formatters;
+import org.netbeans.modules.debugger.jpda.expr.formatters.FormattersLoopControl;
+import org.netbeans.modules.debugger.jpda.expr.formatters.VariablesFormatter;
+import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.InvalidStackFrameExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ObjectCollectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.ui.values.ComputeInlineValues;
+import org.netbeans.modules.debugger.jpda.ui.values.ComputeInlineValues.InlineVariable;
+import org.netbeans.modules.parsing.spi.TaskIndexingMode;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.debugger.ui.Constants;
+import org.netbeans.spi.editor.highlighting.HighlightsLayer;
+import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory;
+import org.netbeans.spi.editor.highlighting.ZOrder;
+import org.netbeans.spi.editor.highlighting.support.OffsetsBag;
+import org.openide.cookies.EditorCookie;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.RequestProcessor;
+import org.openide.util.WeakListeners;
+import org.openide.util.lookup.ServiceProvider;
+import org.openide.util.lookup.ServiceProviders;
+
+
+@DebuggerServiceRegistration(path="netbeans-JPDASession/inlineValue", types=InlineValueComputer.class)
+public class InlineValueComputerImpl implements InlineValueComputer, PreferenceChangeListener, PropertyChangeListener {
+
+    private static final Logger LOG = Logger.getLogger(InlineValueComputerImpl.class.getName());
+    private static final RequestProcessor EVALUATOR = new RequestProcessor(InlineValueComputerImpl.class.getName(), 1, false, false);
+    private static final String JAVA_STRATUM = "Java"; //XXX: this is probably already defined somewhere
+    private final JPDADebuggerImpl debugger;
+    private final Preferences prefs;
+    private TaskDescription currentTask;
+
+    public InlineValueComputerImpl(ContextProvider contextProvider) {
+        debugger = (JPDADebuggerImpl) contextProvider.lookupFirst(null, JPDADebugger.class);
+        debugger.addPropertyChangeListener(this);
+        prefs = MimeLookup.getLookup("text/x-java").lookup(Preferences.class);
+        prefs.addPreferenceChangeListener(WeakListeners.create(PreferenceChangeListener.class, this, prefs));
+    }
+
+    @Override
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (JPDADebugger.PROP_STATE.equals(evt.getPropertyName()) &&
+            debugger.getState() == JPDADebugger.STATE_DISCONNECTED) {
+            setNewTask(null);
+        }
+
+        if (JPDADebugger.PROP_CURRENT_CALL_STACK_FRAME.equals(evt.getPropertyName())) {
+            refreshVariables();
+        }
+    }
+
+    @Override
+    public void preferenceChange(PreferenceChangeEvent evt) {
+        refreshVariables();
+    }
+
+    private void refreshVariables() {
+        CallStackFrame frame = debugger.getCurrentCallStackFrame();
+
+        FileObject frameFile = null;
+        int frameLineNumber = -1;
+        Document frameDocument = null;
+
+        if (prefs.getBoolean(Constants.KEY_INLINE_VALUES, Constants.DEF_INLINE_VALUES) &&
+            frame != null && !frame.isObsolete() &&
+            frame.getThread().isSuspended() &&
+            JAVA_STRATUM.equals(frame.getDefaultStratum())) {
+            try {
+                String url = debugger.getEngineContext().getURL(frame, JAVA_STRATUM);
+                frameFile = url != null ? URLMapper.findFileObject(URI.create(url).toURL()) : null;
+                if (frameFile != null) {
+                    frameLineNumber = frame.getLineNumber(JAVA_STRATUM);
+                    EditorCookie ec = frameFile.getLookup().lookup(EditorCookie.class);
+                    frameDocument = ec != null ? ec.getDocument() : null;
+                }
+            } catch (InternalExceptionWrapper | InvalidStackFrameExceptionWrapper | ObjectCollectedExceptionWrapper | VMDisconnectedExceptionWrapper | MalformedURLException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        }
+
+        TaskDescription newTask;
+
+        if (frameFile != null && frameDocument != null) {
+            newTask = new TaskDescription(frameFile, frameLineNumber, frameDocument);
+        } else {
+            newTask = null;
+        }
+
+        if (setNewTask(newTask)) {
+            return;
+        }
+
+        if (newTask != null) {
+            CountDownLatch computationDone = new CountDownLatch(1);
+
+            newTask.addCancelCallback(computationDone::countDown);
+
+            AtomicReference<Collection<InlineVariable>> values = new AtomicReference<>();
+
+            EVALUATOR.post(() -> {
+                OffsetsBag runningBag = new OffsetsBag(newTask.frameDocument);
+
+                Lookup.getDefault().lookup(ComputeInlineVariablesFactory.class).set(newTask.frameFile, newTask.frameLineNumber, variables -> {
+                    values.set(variables);
+                    computationDone.countDown();
+                });
+
+                try {
+                    computationDone.await();
+                } catch (InterruptedException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+
+                if (newTask.isCancelled()) {
+                    return ;
+                }
+
+                Collection<InlineVariable> variables = values.get();
+
+                if (values == null) {
+                    return ;
+                }
+
+                Map<String, Variable> expression2Value = new HashMap<>();
+                Map<Integer, Map<String, String>> line2Values = new HashMap<>();
+
+                for (InlineVariable v : variables) {
+                    if (newTask.isCancelled()) {
+                        return ;
+                    }
+
+                    Variable value = expression2Value.computeIfAbsent(v.expression(), expr -> {
+                        try {
+                            return debugger.evaluate(expr);
+                        } catch (InvalidExpressionException ex) {
+                            //the variable may not exist
+                            LOG.log(Level.FINE, null, ex);
+                            return null;
+                        }
+                    });
+                    if (value != null) {
+                        String valueText;
+                        if (value instanceof ObjectVariable ov) {
+                            valueText = toValue(ov).replace("\n", "\\n");
+                        } else {
+                            valueText = value.getValue();
+                        }
+                        line2Values.computeIfAbsent(v.lineEnd(), __ -> new LinkedHashMap<>())
+                                   .putIfAbsent(v.expression(), v.expression() + " = " + valueText);
+                        String mergedValues = line2Values.get(v.lineEnd()).values().stream().collect(Collectors.joining(", ", "  ", ""));
+                        AttributeSet attrs = AttributesUtilities.createImmutable("virtual-text-prepend", mergedValues);
+
+                        runningBag.addHighlight(v.lineEnd(), v.lineEnd() + 1, attrs);
+
+                        setHighlights(newTask, runningBag);
+                    }
+                }
+            });
+        }
+    }
+
+    private String toValue(ObjectVariable variable) {
+        //mostly copied from the VariablesFormatterFilter.getValueAt:
+        FormattersLoopControl formattersLoop = new FormattersLoopControl();
+        String type = variable.getType ();
+        ObjectVariable ov = (ObjectVariable) variable;
+        JPDAClassType ct = ov.getClassType();
+        if (ct == null) {
+            return ov.getValue();
+        }
+        VariablesFormatter f = Formatters.getFormatterForType(ct, formattersLoop.getFormatters());
+        String[] formattersInLoopRef = new String[] { null };
+        if (f != null && formattersLoop.canUse(f, ct.getName(), formattersInLoopRef)) {
+            String code = f.getValueFormatCode();
+            if (code != null && code.length() > 0) {
+                try {
+                    java.lang.reflect.Method evaluateMethod = ov.getClass().getMethod("evaluate", String.class);
+                    evaluateMethod.setAccessible(true);
+                    Variable ret = (Variable) evaluateMethod.invoke(ov, code);
+                    if (ret == null) {
+                        return null;
+                    }
+                    return ret.getValue();
+                } catch (java.lang.reflect.InvocationTargetException itex) {
+                    Throwable t = itex.getTargetException();
+                    if (t instanceof InvalidExpressionException) {
+                        return ov.getValue();
+                    } else {
+                        Exceptions.printStackTrace(t);
+                    }
+                } catch (NoSuchMethodException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (SecurityException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (IllegalAccessException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (IllegalArgumentException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+            }
+        } else if (formattersInLoopRef[0] != null) {
+            //ignore loops(?)
+        }
+        if (VariablesFormatterFilter.isToStringValueType(type)) {
+            try {
+                return "\""+ov.getToStringValue ()+"\"";
+            } catch (InvalidExpressionException ex) {
+                // Not a supported operation (e.g. J2ME, see #45543)
+                // Or missing context or any other reason
+                Logger.getLogger(VariablesFormatterFilter.class.getName()).fine("getToStringValue() "+ex.getLocalizedMessage());
+                if ( (ex.getTargetException () != null) &&
+                     (ex.getTargetException () instanceof

Review Comment:
   null check already handled by instanceof



##########
java/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/ui/models/InlineValueComputerImpl.java:
##########
@@ -0,0 +1,475 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.debugger.jpda.ui.models;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.prefs.PreferenceChangeEvent;
+import java.util.prefs.PreferenceChangeListener;
+import java.util.prefs.Preferences;
+import java.util.stream.Collectors;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Document;
+import org.netbeans.api.debugger.DebuggerManagerAdapter;
+import org.netbeans.api.debugger.LazyDebuggerManagerListener;
+import org.netbeans.api.debugger.Session;
+import org.netbeans.api.debugger.jpda.CallStackFrame;
+import org.netbeans.api.debugger.jpda.InvalidExpressionException;
+import org.netbeans.api.debugger.jpda.JPDAClassType;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.ObjectVariable;
+import org.netbeans.api.debugger.jpda.Variable;
+import org.netbeans.api.editor.mimelookup.MimeLookup;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.editor.settings.AttributesUtilities;
+import org.netbeans.api.java.source.CancellableTask;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.JavaSource.Phase;
+import org.netbeans.api.java.source.JavaSource.Priority;
+import org.netbeans.api.java.source.JavaSourceTaskFactory;
+import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
+import org.netbeans.modules.debugger.jpda.expr.formatters.Formatters;
+import org.netbeans.modules.debugger.jpda.expr.formatters.FormattersLoopControl;
+import org.netbeans.modules.debugger.jpda.expr.formatters.VariablesFormatter;
+import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.InvalidStackFrameExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ObjectCollectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.ui.values.ComputeInlineValues;
+import org.netbeans.modules.debugger.jpda.ui.values.ComputeInlineValues.InlineVariable;
+import org.netbeans.modules.parsing.spi.TaskIndexingMode;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.debugger.ui.Constants;
+import org.netbeans.spi.editor.highlighting.HighlightsLayer;
+import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory;
+import org.netbeans.spi.editor.highlighting.ZOrder;
+import org.netbeans.spi.editor.highlighting.support.OffsetsBag;
+import org.openide.cookies.EditorCookie;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.RequestProcessor;
+import org.openide.util.WeakListeners;
+import org.openide.util.lookup.ServiceProvider;
+import org.openide.util.lookup.ServiceProviders;
+
+
+@DebuggerServiceRegistration(path="netbeans-JPDASession/inlineValue", types=InlineValueComputer.class)
+public class InlineValueComputerImpl implements InlineValueComputer, PreferenceChangeListener, PropertyChangeListener {
+
+    private static final Logger LOG = Logger.getLogger(InlineValueComputerImpl.class.getName());
+    private static final RequestProcessor EVALUATOR = new RequestProcessor(InlineValueComputerImpl.class.getName(), 1, false, false);
+    private static final String JAVA_STRATUM = "Java"; //XXX: this is probably already defined somewhere
+    private final JPDADebuggerImpl debugger;
+    private final Preferences prefs;
+    private TaskDescription currentTask;
+
+    public InlineValueComputerImpl(ContextProvider contextProvider) {
+        debugger = (JPDADebuggerImpl) contextProvider.lookupFirst(null, JPDADebugger.class);
+        debugger.addPropertyChangeListener(this);
+        prefs = MimeLookup.getLookup("text/x-java").lookup(Preferences.class);
+        prefs.addPreferenceChangeListener(WeakListeners.create(PreferenceChangeListener.class, this, prefs));
+    }
+
+    @Override
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (JPDADebugger.PROP_STATE.equals(evt.getPropertyName()) &&
+            debugger.getState() == JPDADebugger.STATE_DISCONNECTED) {
+            setNewTask(null);
+        }
+
+        if (JPDADebugger.PROP_CURRENT_CALL_STACK_FRAME.equals(evt.getPropertyName())) {
+            refreshVariables();
+        }
+    }
+
+    @Override
+    public void preferenceChange(PreferenceChangeEvent evt) {
+        refreshVariables();
+    }
+
+    private void refreshVariables() {
+        CallStackFrame frame = debugger.getCurrentCallStackFrame();
+
+        FileObject frameFile = null;
+        int frameLineNumber = -1;
+        Document frameDocument = null;
+
+        if (prefs.getBoolean(Constants.KEY_INLINE_VALUES, Constants.DEF_INLINE_VALUES) &&
+            frame != null && !frame.isObsolete() &&
+            frame.getThread().isSuspended() &&
+            JAVA_STRATUM.equals(frame.getDefaultStratum())) {
+            try {
+                String url = debugger.getEngineContext().getURL(frame, JAVA_STRATUM);
+                frameFile = url != null ? URLMapper.findFileObject(URI.create(url).toURL()) : null;
+                if (frameFile != null) {
+                    frameLineNumber = frame.getLineNumber(JAVA_STRATUM);
+                    EditorCookie ec = frameFile.getLookup().lookup(EditorCookie.class);
+                    frameDocument = ec != null ? ec.getDocument() : null;
+                }
+            } catch (InternalExceptionWrapper | InvalidStackFrameExceptionWrapper | ObjectCollectedExceptionWrapper | VMDisconnectedExceptionWrapper | MalformedURLException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        }
+
+        TaskDescription newTask;
+
+        if (frameFile != null && frameDocument != null) {
+            newTask = new TaskDescription(frameFile, frameLineNumber, frameDocument);
+        } else {
+            newTask = null;
+        }
+
+        if (setNewTask(newTask)) {
+            return;
+        }
+
+        if (newTask != null) {
+            CountDownLatch computationDone = new CountDownLatch(1);
+
+            newTask.addCancelCallback(computationDone::countDown);
+
+            AtomicReference<Collection<InlineVariable>> values = new AtomicReference<>();
+
+            EVALUATOR.post(() -> {
+                OffsetsBag runningBag = new OffsetsBag(newTask.frameDocument);
+
+                Lookup.getDefault().lookup(ComputeInlineVariablesFactory.class).set(newTask.frameFile, newTask.frameLineNumber, variables -> {
+                    values.set(variables);
+                    computationDone.countDown();
+                });
+
+                try {
+                    computationDone.await();
+                } catch (InterruptedException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+
+                if (newTask.isCancelled()) {
+                    return ;
+                }
+
+                Collection<InlineVariable> variables = values.get();
+
+                if (values == null) {
+                    return ;
+                }

Review Comment:
   is the null check supposed to be before the `get()`? or possibly `values` should be `variables`?



##########
java/debugger.jpda.ui/src/org/netbeans/modules/debugger/jpda/ui/models/InlineValueComputerImpl.java:
##########
@@ -0,0 +1,477 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.debugger.jpda.ui.models;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.prefs.PreferenceChangeEvent;
+import java.util.prefs.PreferenceChangeListener;
+import java.util.prefs.Preferences;
+import java.util.stream.Collectors;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Document;
+import org.netbeans.api.debugger.DebuggerManagerAdapter;
+import org.netbeans.api.debugger.LazyDebuggerManagerListener;
+import org.netbeans.api.debugger.Session;
+import org.netbeans.api.debugger.jpda.CallStackFrame;
+import org.netbeans.api.debugger.jpda.InvalidExpressionException;
+import org.netbeans.api.debugger.jpda.JPDAClassType;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.ObjectVariable;
+import org.netbeans.api.debugger.jpda.Variable;
+import org.netbeans.api.editor.mimelookup.MimeLookup;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.editor.settings.AttributesUtilities;
+import org.netbeans.api.java.source.CancellableTask;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.JavaSource.Phase;
+import org.netbeans.api.java.source.JavaSource.Priority;
+import org.netbeans.api.java.source.JavaSourceTaskFactory;
+import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
+import org.netbeans.modules.debugger.jpda.expr.formatters.Formatters;
+import org.netbeans.modules.debugger.jpda.expr.formatters.FormattersLoopControl;
+import org.netbeans.modules.debugger.jpda.expr.formatters.VariablesFormatter;
+import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.InvalidStackFrameExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ObjectCollectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.ui.values.ComputeInlineValues;
+import org.netbeans.modules.debugger.jpda.ui.values.ComputeInlineValues.InlineVariable;
+import org.netbeans.modules.parsing.spi.TaskIndexingMode;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.debugger.ui.Constants;
+import org.netbeans.spi.editor.highlighting.HighlightsLayer;
+import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory;
+import org.netbeans.spi.editor.highlighting.HighlightsSequence;
+import org.netbeans.spi.editor.highlighting.ZOrder;
+import org.netbeans.spi.editor.highlighting.support.OffsetsBag;
+import org.openide.cookies.EditorCookie;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.RequestProcessor;
+import org.openide.util.WeakListeners;
+import org.openide.util.lookup.ServiceProvider;
+import org.openide.util.lookup.ServiceProviders;
+
+
+@DebuggerServiceRegistration(path="netbeans-JPDASession/inlineValue", types=InlineValueComputer.class)
+public class InlineValueComputerImpl implements InlineValueComputer, PreferenceChangeListener, PropertyChangeListener {
+
+    private static final Logger LOG = Logger.getLogger(InlineValueComputerImpl.class.getName());
+    private static final RequestProcessor EVALUATOR = new RequestProcessor(InlineValueComputerImpl.class.getName(), 1, false, false);
+    private static final String JAVA_STRATUM = "Java"; //XXX: this is probably already defined somewhere
+    private final JPDADebuggerImpl debugger;
+    private final Preferences prefs;
+    private TaskDescription currentTask;
+
+    public InlineValueComputerImpl(ContextProvider contextProvider) {
+        debugger = (JPDADebuggerImpl) contextProvider.lookupFirst(null, JPDADebugger.class);
+        debugger.addPropertyChangeListener(this);
+        prefs = MimeLookup.getLookup("text/x-java").lookup(Preferences.class);
+        prefs.addPreferenceChangeListener(WeakListeners.create(PreferenceChangeListener.class, this, prefs));
+    }
+
+    @Override
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (JPDADebugger.PROP_STATE.equals(evt.getPropertyName()) &&
+            debugger.getState() == JPDADebugger.STATE_DISCONNECTED) {
+            setNewTask(null);
+        }
+
+        if (JPDADebugger.PROP_CURRENT_CALL_STACK_FRAME.equals(evt.getPropertyName())) {
+            refreshVariables();
+        }
+    }
+
+    @Override
+    public void preferenceChange(PreferenceChangeEvent evt) {
+        refreshVariables();
+    }
+
+    private void refreshVariables() {
+        CallStackFrame frame = debugger.getCurrentCallStackFrame();
+
+        FileObject frameFile = null;
+        int frameLineNumber = -1;
+        Document frameDocument = null;
+
+        if (prefs.getBoolean(Constants.KEY_INLINE_VALUES, Constants.DEF_INLINE_VALUES) &&
+            frame != null && !frame.isObsolete() &&
+            frame.getThread().isSuspended() &&
+            JAVA_STRATUM.equals(frame.getDefaultStratum())) {
+            try {
+                String url = debugger.getEngineContext().getURL(frame, JAVA_STRATUM);
+                frameFile = url != null ? URLMapper.findFileObject(URI.create(url).toURL()) : null;
+                if (frameFile != null) {
+                    frameLineNumber = frame.getLineNumber(JAVA_STRATUM);
+                    EditorCookie ec = frameFile.getLookup().lookup(EditorCookie.class);
+                    frameDocument = ec != null ? ec.getDocument() : null;
+                }
+            } catch (InternalExceptionWrapper | InvalidStackFrameExceptionWrapper | ObjectCollectedExceptionWrapper | VMDisconnectedExceptionWrapper | MalformedURLException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        }
+
+        TaskDescription newTask;
+
+        if (frameFile != null && frameDocument != null) {
+            newTask = new TaskDescription(frameFile, frameLineNumber, frameDocument);
+        } else {
+            newTask = null;
+        }
+
+        if (setNewTask(newTask)) {
+            return;
+        }
+
+        if (newTask != null) {
+            //TODO: cancel any already running computation if the configuration is different:
+            CountDownLatch computationDone = new CountDownLatch(1);
+
+            newTask.addCancelCallback(computationDone::countDown);
+
+            AtomicReference<Collection<InlineVariable>> values = new AtomicReference<>();
+
+            EVALUATOR.post(() -> {
+                OffsetsBag runningBag = new OffsetsBag(newTask.frameDocument);
+
+                Lookup.getDefault().lookup(ComputeInlineVariablesFactory.class).set(newTask.frameFile, newTask.frameLineNumber, variables -> {
+                    values.set(variables);
+                    computationDone.countDown();
+                });
+
+                try {
+                    computationDone.await();
+                } catch (InterruptedException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+
+                if (newTask.isCancelled()) {
+                    return ;
+                }
+
+                Collection<InlineVariable> variables = values.get();
+
+                if (values == null) {
+                    return ;
+                }
+
+                Map<String, Variable> expression2Value = new HashMap<>();
+                Map<Integer, Map<String, String>> line2Values = new HashMap<>();
+
+                for (InlineVariable v : variables) {
+                    if (newTask.isCancelled()) {
+                        return ;
+                    }
+
+                    Variable value = expression2Value.computeIfAbsent(v.expression, expr -> {
+                        try {
+                            return debugger.evaluate(expr);
+                        } catch (InvalidExpressionException ex) {
+                            //the variable may not exist
+                            LOG.log(Level.FINE, null, ex);
+                            return null;
+                        }
+                    });
+                    if (value != null) {
+                        String valueText;
+                        if (value instanceof ObjectVariable ov) {
+                            valueText = toValue(ov).replace("\n", "\\n");
+                        } else {
+                            valueText = value.getValue();
+                        }
+                        line2Values.computeIfAbsent(v.lineEnd, __ -> new LinkedHashMap<>())
+                                   .putIfAbsent(v.expression, v.expression + " = " + valueText);
+                        String mergedValues = line2Values.get(v.lineEnd).values().stream().collect(Collectors.joining(", ", "  ", ""));
+                        AttributeSet attrs = AttributesUtilities.createImmutable("virtual-text-prepend", mergedValues);
+
+                        runningBag.addHighlight(v.lineEnd, v.lineEnd + 1, attrs);
+
+                        setHighlights(newTask, runningBag);
+                    }
+                }
+            });
+        }
+    }
+
+    private String toValue(ObjectVariable variable) {
+        //mostly copied from the VariablesFormatterFilter.getValueAt:
+        FormattersLoopControl formattersLoop = new FormattersLoopControl();
+        String type = variable.getType ();
+        ObjectVariable ov = (ObjectVariable) variable;
+        JPDAClassType ct = ov.getClassType();
+        if (ct == null) {
+            return ov.getValue();
+        }
+        VariablesFormatter f = Formatters.getFormatterForType(ct, formattersLoop.getFormatters());
+        String[] formattersInLoopRef = new String[] { null };
+        if (f != null && formattersLoop.canUse(f, ct.getName(), formattersInLoopRef)) {
+            String code = f.getValueFormatCode();
+            if (code != null && code.length() > 0) {
+                try {
+                    java.lang.reflect.Method evaluateMethod = ov.getClass().getMethod("evaluate", String.class);
+                    evaluateMethod.setAccessible(true);
+                    Variable ret = (Variable) evaluateMethod.invoke(ov, code);
+                    if (ret == null) {
+                        return null;
+                    }
+                    return ret.getValue();
+                } catch (java.lang.reflect.InvocationTargetException itex) {
+                    Throwable t = itex.getTargetException();
+                    if (t instanceof InvalidExpressionException) {
+                        return ov.getValue();
+                    } else {
+                        Exceptions.printStackTrace(t);
+                    }
+                } catch (NoSuchMethodException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (SecurityException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (IllegalAccessException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (IllegalArgumentException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+            }
+        } else if (formattersInLoopRef[0] != null) {
+            //ignore loops(?)
+        }
+        if (VariablesFormatterFilter.isToStringValueType(type)) {
+            try {
+                return "\""+ov.getToStringValue ()+"\"";
+            } catch (InvalidExpressionException ex) {
+                // Not a supported operation (e.g. J2ME, see #45543)
+                // Or missing context or any other reason
+                Logger.getLogger(VariablesFormatterFilter.class.getName()).fine("getToStringValue() "+ex.getLocalizedMessage());
+                if ( (ex.getTargetException () != null) &&
+                     (ex.getTargetException () instanceof
+                       UnsupportedOperationException)
+                ) {
+                    // PATCH for J2ME. see 45543
+                    return ov.getValue();
+                }
+                return ex.getLocalizedMessage ();
+            }
+        }
+        return ov.getValue();
+    }
+
+    private synchronized boolean setNewTask(TaskDescription newTask) {
+        if (Objects.equals(currentTask, newTask)) {
+            return true; //nothing changed, nothing to do
+        }
+
+        if (currentTask != null) {
+            currentTask.cancel();
+            getHighlightsBag(currentTask.frameDocument).clear();
+        }
+
+        currentTask = newTask;
+
+        return false;
+    }
+
+    private synchronized void setHighlights(TaskDescription task, OffsetsBag highlights) {
+        if (!task.isCancelled()) {
+            getHighlightsBag(currentTask.frameDocument).setHighlights(highlights);
+        }
+    }
+
+    @DebuggerServiceRegistration(types=LazyDebuggerManagerListener.class)
+    public static final class Init extends DebuggerManagerAdapter {
+        @Override
+        public void sessionAdded(Session session) {
+            for (InlineValueComputer v : session.lookup("inlineValue", InlineValueComputer.class)) {
+            }

Review Comment:
   what is the purpose of the empty loop?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Inlining values inside debugger [netbeans]

Posted by "lahodaj (via GitHub)" <gi...@apache.org>.
lahodaj commented on PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#issuecomment-2900365841

   > what is the purpose of the empty loop?
   
   It was there to initialize the computer. But turned out I can simply instantiate it, and it (hopefully) works as well, using `Session` instead of `ContextProvider`).


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Inlining values inside debugger [netbeans]

Posted by "mbien (via GitHub)" <gi...@apache.org>.
mbien commented on PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#issuecomment-2901048166

   > > what is the purpose of the empty loop?
   > 
   > It was there to initialize the computer. But turned out I can simply instantiate it, and it (hopefully) works as well, using `Session` instead of `ContextProvider`).
   
   I guessed that something like that might have been the reason for it. I was just thinking since lookup returns a List and no lazily initialized structure, it could have been already sufficient without the loop itself. But the new impl is more direct and less mysterious - thanks.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


Re: [PR] Inlining values inside debugger [netbeans]

Posted by "mbien (via GitHub)" <gi...@apache.org>.
mbien commented on PR #8019:
URL: https://github.com/apache/netbeans/pull/8019#issuecomment-2912590262

   great feature! I agree that it would be good to get it in. Don't forget to squash ;)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists