Skip to content

Commit 48b962b

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 64136ca commit 48b962b

18 files changed

+579
-2
lines changed

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -864,10 +864,13 @@ public static IProcess newProcess(ILaunch launch, Process process, String label)
864864
*/
865865
public static IProcess newProcess(ILaunch launch, Process process, String label, Map<String, String> attributes) {
866866
ILaunchConfiguration config= launch.getLaunchConfiguration();
867-
String processFactoryID= null;
867+
String processFactoryID = null;
868+
// FIXME hack for demo purpose we need an UI for the user to decide if a
869+
// terminal
870+
// is wanted
868871
if (config != null) {
869872
try {
870-
processFactoryID= config.getAttribute(ATTR_PROCESS_FACTORY_ID, (String)null);
873+
processFactoryID = config.getAttribute(ATTR_PROCESS_FACTORY_ID, "org.eclipse.debug.terminal.processFactory.cdt"); //$NON-NLS-1$
871874
} catch (CoreException e) {
872875
}
873876
}
@@ -1028,6 +1031,7 @@ public static Process exec(String[] cmdLine, File workingDirectory, String[] env
10281031
// environment variables is slightly different between
10291032
// ProcessBuilder and Runtime.exec only the new option uses process
10301033
// builder to not break existing caller of this method
1034+
10311035
if (mergeOutput) {
10321036
ProcessBuilder pb = new ProcessBuilder(cmdLine);
10331037
directory.ifPresent(pb::directory);
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: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
<buildCommand>
24+
<name>org.eclipse.pde.ds.core.builder</name>
25+
<arguments>
26+
</arguments>
27+
</buildCommand>
28+
</buildSpec>
29+
<natures>
30+
<nature>org.eclipse.pde.PluginNature</nature>
31+
<nature>org.eclipse.jdt.core.javanature</nature>
32+
</natures>
33+
</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: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
dsVersion=V1_4
2+
eclipse.preferences.version=1
3+
enabled=true
4+
generateBundleActivationPolicyLazy=true
5+
path=OSGI-INF
6+
validationErrorLevel=error
7+
validationErrorLevel.missingImplicitUnbindMethod=error
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
Manifest-Version: 1.0
2+
Bundle-ManifestVersion: 2
3+
Bundle-Name: Terminal Session Support for Eclipse
4+
Bundle-SymbolicName: org.eclipse.debug.terminal;singleton:=true
5+
Bundle-Version: 1.0.0.qualifier
6+
Import-Package: com.pty4j;version="0.13.2"
7+
Require-Bundle: org.eclipse.core.runtime,
8+
org.eclipse.debug.core;bundle-version="3.23.0",
9+
org.eclipse.tm.terminal.control;bundle-version="5.5.301",
10+
org.eclipse.cdt.core.native;bundle-version="6.3.401",
11+
org.eclipse.swt;bundle-version="3.130.0",
12+
org.eclipse.ui;bundle-version="3.207.100"
13+
Bundle-RequiredExecutionEnvironment: JavaSE-21
14+
Automatic-Module-Name: org.eclipse.debug.terminal
15+
Bundle-ActivationPolicy: lazy
16+
Service-Component: OSGI-INF/org.eclipse.debug.terminal.ui.PageBookAdapter.xml
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.4.0" name="org.eclipse.debug.terminal.ui.PageBookAdapter">
3+
<property name="adaptableClass" type="String" value="org.eclipse.debug.terminal.PtyRuntimeProcess"/>
4+
<property name="adapterNames" type="String" value="org.eclipse.ui.part.IPageBookViewPage"/>
5+
<service>
6+
<provide interface="org.eclipse.core.runtime.IAdapterFactory"/>
7+
</service>
8+
<implementation class="org.eclipse.debug.terminal.ui.PageBookAdapter"/>
9+
</scr:component>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
source.. = src/
2+
output.. = bin/
3+
bin.includes = META-INF/,\
4+
.,\
5+
plugin.xml,\
6+
OSGI-INF/org.eclipse.debug.terminal.ui.PageBookAdapter.xml
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<?eclipse version="3.4"?>
3+
<plugin>
4+
<extension
5+
point="org.eclipse.debug.core.execFactories">
6+
<execFactory
7+
class="org.eclipse.debug.terminal.PtyExecFactory"
8+
id="org.eclipse.debug.terminal.execFactory1"
9+
priority="100">
10+
</execFactory>
11+
<execFactory
12+
class="org.eclipse.debug.terminal.Pty4jExecFactory"
13+
id="org.eclipse.debug.terminal.execFactory2"
14+
priority="0">
15+
</execFactory>
16+
</extension>
17+
<extension
18+
point="org.eclipse.debug.core.processFactories">
19+
<processFactory
20+
class="org.eclipse.debug.terminal.PtyProcessFactory"
21+
id="org.eclipse.debug.terminal.processFactory.cdt">
22+
</processFactory>
23+
</extension>
24+
25+
</plugin>
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 java.io.File;
17+
import java.io.IOException;
18+
import java.util.Map;
19+
import java.util.Optional;
20+
21+
import org.eclipse.core.runtime.CoreException;
22+
import org.eclipse.core.runtime.Status;
23+
import org.eclipse.debug.core.ExecFactory;
24+
25+
import com.pty4j.PtyProcessBuilder;
26+
27+
class Pty4jExecFactory implements ExecFactory {
28+
29+
@Override
30+
public Optional<Process> exec(String[] cmdLine, Optional<File> workingDirectory,
31+
Optional<Map<String, String>> environment, boolean mergeOutput) throws CoreException {
32+
try {
33+
PtyProcessBuilder builder = new PtyProcessBuilder().setRedirectErrorStream(true).setInitialRows(80)
34+
.setInitialColumns(25).setCommand(cmdLine).setEnvironment(environment.orElse(System.getenv()));
35+
workingDirectory.map(File::getAbsolutePath).ifPresent(builder::setDirectory);
36+
return Optional.of(builder.start());
37+
} catch (IOException e) {
38+
throw new CoreException(Status.error("Can't start process", e));
39+
} catch (RuntimeException e) {
40+
return Optional.empty();
41+
}
42+
}
43+
44+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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 java.io.File;
17+
import java.io.IOException;
18+
import java.util.Map;
19+
import java.util.Optional;
20+
21+
import org.eclipse.cdt.utils.pty.PTY;
22+
import org.eclipse.cdt.utils.pty.PTY.Mode;
23+
import org.eclipse.cdt.utils.spawner.ProcessFactory;
24+
import org.eclipse.core.runtime.CoreException;
25+
import org.eclipse.core.runtime.Status;
26+
import org.eclipse.debug.core.ExecFactory;
27+
28+
public class PtyExecFactory implements ExecFactory {
29+
30+
@Override
31+
public Optional<Process> exec(String[] cmdLine, Optional<File> workingDirectory,
32+
Optional<Map<String, String>> environment, boolean mergeOutput) throws CoreException {
33+
if (mergeOutput || !PTY.isSupported(Mode.TERMINAL)) {
34+
return Optional.empty();
35+
}
36+
try {
37+
PTY pty = new PTY(Mode.TERMINAL);
38+
pty.setTerminalSize(80, 24);
39+
String[] env;
40+
if (environment.isEmpty()) {
41+
env = null;
42+
} else {
43+
env = environment.stream().flatMap(m -> m.entrySet().stream()).map(e -> e.getKey() + "=" + e.getValue())
44+
.toArray(String[]::new);
45+
}
46+
File wd = workingDirectory.orElse(null);
47+
return Optional.of(ProcessFactory.getFactory().exec(cmdLine, env, wd, pty));
48+
} catch (IOException e) {
49+
throw new CoreException(Status.error("Execution failed", e));
50+
}
51+
}
52+
53+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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 java.util.Map;
17+
18+
import org.eclipse.debug.core.ILaunch;
19+
import org.eclipse.debug.core.IProcessFactory;
20+
import org.eclipse.debug.core.model.IProcess;
21+
22+
public class PtyProcessFactory implements IProcessFactory {
23+
24+
@Override
25+
public IProcess newProcess(ILaunch launch, Process process, String label, Map<String, String> attributes) {
26+
return new PtyRuntimeProcess(launch, process, label, attributes);
27+
}
28+
29+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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 java.util.Map;
17+
18+
import org.eclipse.cdt.utils.spawner.Spawner;
19+
import org.eclipse.debug.core.ILaunch;
20+
import org.eclipse.debug.core.model.RuntimeProcess;
21+
22+
public class PtyRuntimeProcess extends RuntimeProcess {
23+
24+
public PtyRuntimeProcess(ILaunch launch, Process process, String name, Map<String, String> attributes) {
25+
super(launch, process, name, attributes);
26+
}
27+
28+
public Spawner getSpawner() {
29+
Process systemProcess = super.getSystemProcess();
30+
if (systemProcess instanceof Spawner) {
31+
return (Spawner) systemProcess;
32+
}
33+
return null;
34+
}
35+
36+
}

0 commit comments

Comments
 (0)