Skip to content

Commit fb1e394

Browse files
committed
Add support for launching Eclipse Process in a system terminal session
Currently processes are launched as a forked process without a terminal session attached to them. Some features of processes require a terminal session (e.g. autocompletion) and currently not work when running from within eclipse (e.g. the gogo-shell). This is an attempt to bring terminal session support to Eclipse to support real terminal application similar to what is supported by IntelliJ.
1 parent 0d9f518 commit fb1e394

File tree

12 files changed

+266
-7
lines changed

12 files changed

+266
-7
lines changed

debug/org.eclipse.debug.core/META-INF/MANIFEST.MF

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
22
Bundle-ManifestVersion: 2
33
Bundle-Name: %pluginName
44
Bundle-SymbolicName: org.eclipse.debug.core; singleton:=true
5-
Bundle-Version: 3.22.100.qualifier
5+
Bundle-Version: 3.23.0.qualifier
66
Bundle-Activator: org.eclipse.debug.core.DebugPlugin
77
Bundle-Vendor: %providerName
88
Bundle-Localization: plugin

debug/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,8 @@ public class DebugPlugin extends Plugin {
388388
*/
389389
private static DebugPlugin fgDebugPlugin= null;
390390

391+
static ExecFactory factory;
392+
391393
/**
392394
* The singleton breakpoint manager.
393395
*/
@@ -981,7 +983,13 @@ public static Process exec(String[] cmdLine, File workingDirectory, String[] env
981983
* @since 3.14
982984
*/
983985
public static Process exec(String[] cmdLine, File workingDirectory, String[] envp, boolean mergeOutput) throws CoreException {
984-
Process p = null;
986+
ExecFactory builder;
987+
synchronized (DebugPlugin.class) {
988+
builder = factory;
989+
}
990+
if (builder != null) {
991+
return builder.exec(cmdLine, shortenWindowsPath(workingDirectory), envp, mergeOutput);
992+
}
985993
try {
986994
// starting with and without merged output could be done with the
987995
// same process builder approach but since the handling of
@@ -1004,11 +1012,11 @@ public static Process exec(String[] cmdLine, File workingDirectory, String[] env
10041012
}
10051013
}
10061014
}
1007-
p = pb.start();
1015+
return pb.start();
10081016
} else if (workingDirectory == null) {
1009-
p = Runtime.getRuntime().exec(cmdLine, envp);
1017+
return Runtime.getRuntime().exec(cmdLine, envp);
10101018
} else {
1011-
p = Runtime.getRuntime().exec(cmdLine, envp, shortenWindowsPath(workingDirectory));
1019+
return Runtime.getRuntime().exec(cmdLine, envp, shortenWindowsPath(workingDirectory));
10121020
}
10131021
} catch (IOException e) {
10141022
Status status = new Status(IStatus.ERROR, getUniqueIdentifier(), ERROR, DebugCoreMessages.DebugPlugin_0, e);
@@ -1021,17 +1029,20 @@ public static Process exec(String[] cmdLine, File workingDirectory, String[] env
10211029
if (handler != null) {
10221030
Object result = handler.handleStatus(status, null);
10231031
if (result instanceof Boolean resultValue && resultValue) {
1024-
p = exec(cmdLine, null);
1032+
return exec(cmdLine, null);
10251033
}
10261034
}
10271035
}
1028-
return p;
1036+
return null;
10291037
}
10301038

10311039
// https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
10321040
private static final int WINDOWS_MAX_PATH = 258;
10331041

10341042
private static File shortenWindowsPath(File path) {
1043+
if (path == null) {
1044+
return null;
1045+
}
10351046
if (path.getPath().length() > WINDOWS_MAX_PATH && Platform.OS.isWindows()) {
10361047
// When spawning new processes on Windows, there is no uniform way
10371048
// to use long working directory paths that exceed the default path
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Christoph Läubrich and others.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* Christoph Läubrich - initial API and implementation
13+
*******************************************************************************/
14+
package org.eclipse.debug.core;
15+
16+
import java.io.File;
17+
18+
import org.eclipse.core.runtime.CoreException;
19+
20+
/**
21+
* A {@link ExecFactory} can be used to control how Eclipse forks a
22+
* new {@link Process}. As this is a global behavior, only one factory can be
23+
* set for the whole application lifetime.
24+
*
25+
* @since 3.23
26+
*/
27+
public interface ExecFactory {
28+
29+
Process exec(String[] cmdLine, File workingDirectory, String[] envp, boolean mergeOutput) throws CoreException;
30+
31+
static void setDefault(ExecFactory factory) {
32+
synchronized (DebugPlugin.class) {
33+
if (DebugPlugin.factory != null) {
34+
throw new IllegalStateException("A factory was already set for this application"); //$NON-NLS-1$
35+
}
36+
DebugPlugin.factory = factory;
37+
}
38+
}
39+
40+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<classpath>
3+
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21"/>
4+
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
5+
<classpathentry kind="src" path="src"/>
6+
<classpathentry kind="output" path="bin"/>
7+
</classpath>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<projectDescription>
3+
<name>org.eclipse.debug.terminal</name>
4+
<comment></comment>
5+
<projects>
6+
</projects>
7+
<buildSpec>
8+
<buildCommand>
9+
<name>org.eclipse.jdt.core.javabuilder</name>
10+
<arguments>
11+
</arguments>
12+
</buildCommand>
13+
<buildCommand>
14+
<name>org.eclipse.pde.ManifestBuilder</name>
15+
<arguments>
16+
</arguments>
17+
</buildCommand>
18+
<buildCommand>
19+
<name>org.eclipse.pde.SchemaBuilder</name>
20+
<arguments>
21+
</arguments>
22+
</buildCommand>
23+
</buildSpec>
24+
<natures>
25+
<nature>org.eclipse.pde.PluginNature</nature>
26+
<nature>org.eclipse.jdt.core.javanature</nature>
27+
</natures>
28+
</projectDescription>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
eclipse.preferences.version=1
2+
encoding/<project>=UTF-8
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
eclipse.preferences.version=1
2+
org.eclipse.jdt.core.compiler.codegen.targetPlatform=21
3+
org.eclipse.jdt.core.compiler.compliance=21
4+
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
5+
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
6+
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
7+
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
8+
org.eclipse.jdt.core.compiler.release=enabled
9+
org.eclipse.jdt.core.compiler.source=21
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Manifest-Version: 1.0
2+
Bundle-ManifestVersion: 2
3+
Bundle-Name: Terminal Session Support for Eclipse
4+
Bundle-SymbolicName: org.eclipse.debug.terminal
5+
Bundle-Version: 1.0.0.qualifier
6+
Import-Package: com.pty4j;version="0.13.2"
7+
Bundle-Activator: org.eclipse.debug.terminal.Activator
8+
Require-Bundle: org.eclipse.core.runtime,
9+
org.eclipse.debug.core;bundle-version="3.23.0",
10+
org.eclipse.tm.terminal.control;bundle-version="5.5.301",
11+
org.eclipse.cdt.core.native;bundle-version="6.3.401"
12+
Bundle-RequiredExecutionEnvironment: JavaSE-21
13+
Automatic-Module-Name: org.eclipse.debug.terminal
14+
Bundle-ActivationPolicy: lazy
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
source.. = src/
2+
output.. = bin/
3+
bin.includes = META-INF/,\
4+
.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Christoph Läubrich and others.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* Christoph Läubrich - initial API and implementation
13+
*******************************************************************************/
14+
package org.eclipse.debug.terminal;
15+
16+
import org.eclipse.debug.core.ExecFactory;
17+
import org.osgi.framework.BundleActivator;
18+
import org.osgi.framework.BundleContext;
19+
20+
public class Activator implements BundleActivator {
21+
22+
private boolean cdt = true;
23+
24+
@Override
25+
public void start(BundleContext bundleContext) {
26+
try {
27+
System.out.println("Activate terminal support...");
28+
if (cdt) {
29+
System.out.println(" ... with cdt!");
30+
ExecFactory.setDefault(new CDTFactory());
31+
} else {
32+
System.out.println(" ... with pty4j!");
33+
ExecFactory.setDefault(new Pty4jExecFactory());
34+
}
35+
} catch (Throwable t) {
36+
System.err.println("Can't activate terminal support");
37+
}
38+
}
39+
40+
@Override
41+
public void stop(BundleContext bundleContext) throws Exception {
42+
}
43+
44+
}

0 commit comments

Comments
 (0)