Skip to content

Commit

Permalink
fix: Run/Debug with contextual menu
Browse files Browse the repository at this point in the history
Fixes redhat-developer#760

Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
angelozerr committed Jan 25, 2025
1 parent 2ff3a13 commit ca2af31
Show file tree
Hide file tree
Showing 23 changed files with 498 additions and 152 deletions.
6 changes: 6 additions & 0 deletions docs/dap/DAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ You should edit the variable:
the edit apply will consume the
[SetVariable request](https://microsoft.github.io/debug-adapter-protocol//specification.html#Requests_SetVariable):

# Contextual Menu

Click on right button open existing / new DAP run configuration:

![Run/Debug menu](images/DAP_contextual_menu.png)

## Templates

LSP4IJ provides DAP templates that allow to initialize a given DAP server very quickly:
Expand Down
Binary file added docs/dap/images/DAP_contextual_menu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/dap/images/vscode-js-debug/debug_ts.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
72 changes: 70 additions & 2 deletions docs/dap/user-defined-dap/vscode-js-debug.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ As you have selected `VSCode JS Debug` server, it will automatically populate th
- the `working directory` (usually the project's root directory)
- the path to the `test.js` file.

![DAP Configuration Type/Configuration](../images/DAP_config_type_configuration.png)
![DAP Configuration Type/Configuration](../images/vscode-js-debug/configuration_tab.png)

2. Select `Launch` as debugging type.
3. The DAP parameters of the launch should look like this:
Expand Down Expand Up @@ -118,4 +118,72 @@ You will also see `Threads` and `Variables`:

## Configure the TypeScript file to run/debug

TODO : how to configure source maps?
Let’s debugging the following `test.ts` file:

```ts
class Greeter {
greeting: string;

constructor(message: string) {
this.greeting = message;
}

greet() {
return "Hello, " + this.greeting;
}
}

let greeter = new Greeter("world");
console.log(greeter.greet())
```

![Set Breakpoint](../images/vscode-js-debug/set_breakpoint_ts.png)

### Compile TypeScript

Create a `tsconfig.json` file like this:

```json
{
"compilerOptions": {
"target": "ES6",
"module": "CommonJS",
"outDir": "out",
"sourceMap": true
}
}
```

Execute `tsc` command to generate source maps in the `out` folder:

* `test.js`
* `test.js.map`

## Configure the TypeScript file to run/debug

Update the DAP parameters like this:

```json
{
"type": "pwa-node",
"request": "launch",
"program": "${file}",
"cwd": "${workspaceFolder}",
"outFiles": [
"${workspaceFolder}/**/*.(m|c|)js",
"!**/node_modules/**"
],
"sourceMaps": true,
"__workspaceFolder": "${workspaceFolder}"
}
```

Update the path with the `test.ts` file.

![DAP Configuration Type/Configuration](../images/vscode-js-debug/configuration_ts_tab.png)

### Debugging

TypeScript debugging should be available:

![Debugging TypeScript](../images/vscode-js-debug/debug_ts.png)
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
/*******************************************************************************
* Copyright (c) 2025 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package com.redhat.devtools.lsp4ij.dap;

import com.intellij.lang.Language;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.impl.DocumentImpl;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.PlainTextFileType;
import com.intellij.openapi.fileTypes.PlainTextLanguage;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.xdebugger.XExpression;
import com.intellij.xdebugger.evaluation.EvaluationMode;
import com.intellij.xdebugger.evaluation.XDebuggerEditorsProviderBase;
import com.redhat.devtools.lsp4ij.dap.evaluation.DAPExpressionCodeFragment;
import org.jetbrains.annotations.NotNull;
Expand All @@ -31,13 +44,23 @@ public DAPDebuggerEditorsProvider(@Nullable FileType fileType,
return fileType != null ? fileType : PlainTextFileType.INSTANCE;
}

@Override
public @NotNull Document createDocument(@NotNull Project project, @NotNull XExpression expression, @Nullable PsiElement context, @NotNull EvaluationMode mode) {
if (context == null || context.getContainingFile() == null) {
// File is null, returns a dummy document
return new DocumentImpl(expression.getExpression());
}
return super.createDocument(project, expression, context, mode);
}

@Override
protected PsiFile createExpressionCodeFragment(@NotNull Project project,
@NotNull String text,
@Nullable PsiElement context,
boolean isPhysical) {
FileType fileType = getFileType();
Language language = PlainTextLanguage.INSTANCE;
Language language = Language.ANY;
// File should be never null here
PsiFile file = context != null ? context.getContainingFile() : null;
if (file != null) {
// Get file type / language of the file which is debugging when debugger is suspended.
Expand Down
35 changes: 35 additions & 0 deletions src/main/java/com/redhat/devtools/lsp4ij/dap/DAPIJUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*******************************************************************************
* Copyright (c) 2025 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package com.redhat.devtools.lsp4ij.dap;

import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;

/**
* Debug Adapter Protocol (DAP) utilities.
*/
public class DAPIJUtils {

private DAPIJUtils() {

}

@NotNull
public static String getFilePath(@NotNull VirtualFile file) {
return file.toNioPath().toString();
}

@NotNull
public static String getFileName(@NotNull VirtualFile file) {
return file.getName();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
import java.util.*;
import java.util.concurrent.CompletableFuture;

import static com.redhat.devtools.lsp4ij.dap.DAPIJUtils.getFileName;
import static com.redhat.devtools.lsp4ij.dap.DAPIJUtils.getFilePath;

/**
* Debug Adapter Protocol (DAP) breakpoint handler.
*/
Expand Down Expand Up @@ -85,8 +88,8 @@ public void unregisterBreakpoint(@NotNull XLineBreakpoint<DAPBreakpointPropertie
var sourcePosition = breakpoint.getSourcePosition();

Source source = new Source();
source.setPath(getFilePath(sourcePosition));
source.setName(getFileName(sourcePosition));
source.setPath(getFilePath(sourcePosition.getFile()));
source.setName(getFileName(sourcePosition.getFile()));

List<SourceBreakpoint> sourceBreakpoints = targetBreakpoints
.computeIfAbsent(source, s -> new ArrayList<>());
Expand Down Expand Up @@ -142,14 +145,6 @@ private static int getLineNumber(@NotNull XSourcePosition sourcePosition) {
}


private static String getFilePath(@NotNull XSourcePosition sourcePosition) {
return sourcePosition.getFile().getPath();
}

private static String getFileName(@NotNull XSourcePosition sourcePosition) {
return sourcePosition.getFile().getName();
}

@Nullable
public XBreakpoint<DAPBreakpointProperties> findBreakPoint(@NotNull StackFrame stackFrame) {
Path filePath = Paths.get(stackFrame.getSource().getPath().trim());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.xdebugger.breakpoints.XLineBreakpointType;
import com.redhat.devtools.lsp4ij.dap.descriptors.DebugAdapterDescriptorFactoryRegistry;
import com.redhat.devtools.lsp4ij.dap.descriptors.DebugAdapterManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

Expand All @@ -38,7 +38,7 @@ public DAPBreakpointProperties createBreakpointProperties(@NotNull final Virtual
public boolean canPutAt(@NotNull VirtualFile file,
int line,
@NotNull Project project) {
return DebugAdapterDescriptorFactoryRegistry.getInstance().canDebug(file, project);
return DebugAdapterManager.getInstance().canDebug(file, project);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import com.redhat.devtools.lsp4ij.dap.DebuggingType;
import com.redhat.devtools.lsp4ij.dap.descriptors.DebugAdapterDescriptor;
import com.redhat.devtools.lsp4ij.dap.descriptors.DebugAdapterDescriptorFactory;
import com.redhat.devtools.lsp4ij.dap.descriptors.DebugAdapterDescriptorFactoryRegistry;
import com.redhat.devtools.lsp4ij.internal.StringUtils;
import com.redhat.devtools.lsp4ij.launching.ServerMappingSettings;
import com.redhat.devtools.lsp4ij.settings.ServerTrace;
Expand Down Expand Up @@ -220,26 +219,57 @@ public boolean canDebug(@NotNull VirtualFile file) {
return false;
}

/**
* Returns true if the given executor id (ex: 'Debug') can be executed and false otherwise.
*
* @param executorId the executor id (ex: 'Debug').
* @return true if the given executor id (ex: 'Debug') can be executed and false otherwise.
*/
public boolean canRun(@NotNull String executorId) {
var serverFactory = getServerFactory();
return serverFactory != null ? serverFactory.canRun(executorId) : true;
}


@Override
public void checkConfiguration() throws RuntimeConfigurationException {
if (StringUtils.isBlank(getCommand()) && StringUtils.isBlank(getServerId())) {
throw new RuntimeConfigurationException("Server command is required", DEBUG_ADAPTER_CONFIGURATION);
}
}

/**
* Returns the server DAP factory descriptor and null otherwise.
*
* @return the server DAP factory descriptor and null otherwise.
*/
@Nullable
private DebugAdapterDescriptorFactory getServerFactory() {
String serverId = getOptions().getServerId();
if (StringUtils.isBlank(serverId)) {
return null;
}
return DebugAdapterDescriptorFactoryRegistry.getInstance().getFactoryById(serverId);
public DebugAdapterDescriptorFactory getServerFactory() {
return getOptions().getServerFactory();
}

/**
* Copy the configuration into the given configuration.
*
* @param configuration the configuration where values must be copied.
*/
public void copyTo(@NotNull DAPRunConfiguration configuration) {
// Configuration
configuration.setWorkingDirectory(getWorkingDirectory());
configuration.setFile(getFile());
configuration.setDebuggingType(getDebuggingType());
configuration.setLaunchParameters(getLaunchParameters());
configuration.setAttachParameters(getAttachParameters());

// Mappings
configuration.setServerMappings(getServerMappings());

// Server
configuration.setServerId(getServerId());
configuration.setServerName(getServerName());
configuration.setCommand(getCommand());
configuration.setConnectingServerStrategy(getConnectingServerStrategy());
configuration.setWaitForTimeout(getWaitForTimeout());
configuration.setWaitForTrace(getWaitForTrace());
configuration.setServerTrace(getServerTrace());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import com.redhat.devtools.lsp4ij.dap.ConnectingServerStrategy;
import com.redhat.devtools.lsp4ij.dap.DebuggingType;
import com.redhat.devtools.lsp4ij.dap.configurations.extractors.NetworkAddressExtractor;
import com.redhat.devtools.lsp4ij.dap.descriptors.DebugAdapterDescriptorFactory;
import com.redhat.devtools.lsp4ij.dap.descriptors.DebugAdapterManager;
import com.redhat.devtools.lsp4ij.internal.StringUtils;
import com.redhat.devtools.lsp4ij.launching.ServerMappingSettings;
import com.redhat.devtools.lsp4ij.settings.ServerTrace;
Expand Down Expand Up @@ -47,7 +49,7 @@ public class DAPRunConfigurationOptions extends RunConfigurationOptions {
.provideDelegate(this, "attachParameters");

// Mappings settings
private final StoredProperty<List<ServerMappingSettings>> serverMappings = this.<ServerMappingSettings >list()
private final StoredProperty<List<ServerMappingSettings>> serverMappings = this.<ServerMappingSettings>list()
.provideDelegate(this, "serverMappings");

// Server settings
Expand All @@ -59,10 +61,10 @@ public class DAPRunConfigurationOptions extends RunConfigurationOptions {

private final StoredProperty<String> command = string("")
.provideDelegate(this, "command");

private final StoredProperty<String> connectingServerStrategy = string(ConnectingServerStrategy.NONE.name())
.provideDelegate(this, "connectingServerStrategy");

private final StoredProperty<Integer> waitForTimeout = property(0)
.provideDelegate(this, "waitForTimeout");

Expand Down Expand Up @@ -195,7 +197,7 @@ public void setWaitForTrace(String waitForTrace) {
this.waitForTrace.setValue(this, waitForTrace);
this.networkAddressExtractor = null;
}

public int getWaitForTimeout() {
return waitForTimeout.getValue(this);
}
Expand All @@ -222,4 +224,17 @@ public void setServerTrace(ServerTrace serverTrace) {
}
return networkAddressExtractor;
}

/**
* Returns the server DAP factory descriptor and null otherwise.
*
* @return the server DAP factory descriptor and null otherwise.
*/
public @Nullable DebugAdapterDescriptorFactory getServerFactory() {
String serverId = getServerId();
if (StringUtils.isBlank(serverId)) {
return null;
}
return DebugAdapterManager.getInstance().getFactoryById(serverId);
}
}
Loading

0 comments on commit ca2af31

Please sign in to comment.