1919
2020import static org .eclipse .lsp4e .internal .NullSafetyHelper .castNonNull ;
2121
22+ import java .io .BufferedReader ;
2223import java .io .File ;
2324import java .io .IOException ;
25+ import java .io .InputStreamReader ;
2426import java .io .UncheckedIOException ;
2527import java .net .URI ;
2628import java .util .ArrayList ;
@@ -263,6 +265,7 @@ synchronized void close() {
263265 private final ExecutorService dispatcher ;
264266 private final ExecutorService listener ;
265267 private final ExecutorService cleaner ;
268+ private final ExecutorService errorProcessor ;
266269
267270 private LanguageServerContext context = new LanguageServerContext ();
268271
@@ -306,6 +309,11 @@ private LanguageServerWrapper(@Nullable IProject project, LanguageServerDefiniti
306309 final var livenessThreadNameFormat = formatPrefix + "#cleaner" ; //$NON-NLS-1$
307310 this .cleaner = Executors
308311 .newSingleThreadExecutor (new ThreadFactoryBuilder ().setNameFormat (livenessThreadNameFormat ).build ());
312+
313+ // Executor service to run a thread processing the LS error stream.
314+ final var errorsThreadNameFormat = formatPrefix + "#errorProcessor" ; //$NON-NLS-1$
315+ this .errorProcessor = Executors
316+ .newSingleThreadExecutor (new ThreadFactoryBuilder ().setNameFormat (errorsThreadNameFormat ).build ());
309317 }
310318
311319 void stopDispatcher () {
@@ -319,7 +327,10 @@ void stopDispatcher() {
319327 this .listener .shutdownNow ();
320328
321329 // Again only needed for testing as the cleaner should exit when the launcher terminates.
322- this .cleaner .shutdownNow ();
330+ this .cleaner .shutdown ();
331+
332+ // Similarly, the error stream should also close when the input/output streams close.
333+ this .errorProcessor .shutdownNow ();
323334 }
324335
325336 /**
@@ -463,7 +474,7 @@ private synchronized void start(boolean forceRestart) {
463474 if (!initFuture .isDone ()) {
464475 workingContext .languageServer = null ;
465476 initFuture .completeExceptionally (new ExecutionException (
466- "Unexpected language server termination" , launchException )); //$NON-NLS-1$
477+ "Unexpected language server termination: " + getFullErrorStream () , launchException )); //$NON-NLS-1$
467478 }
468479 }, cleaner );
469480 return initFuture ;
@@ -491,6 +502,9 @@ private synchronized void start(boolean forceRestart) {
491502 }
492503 });
493504 FileBuffers .getTextFileBufferManager ().addFileBufferListener (fileBufferListener );
505+ castNonNull (initializeFuture ).thenRunAsync (() -> {
506+ processErrorStream (castNonNull (context .lspStreamProvider ), l -> LanguageServerPlugin .getDefault ().getLog ().error (l ), e -> {throw new UncheckedIOException (e );});
507+ }, errorProcessor );
494508 }
495509 }).exceptionally (e -> {
496510 shutdown (workingContext );
@@ -577,6 +591,30 @@ private CompletableFuture<InitializeResult> initServer(final @Nullable URI rootU
577591 // FIXME race: this.context may not be what it is expected to be, should be parameter
578592 }
579593
594+ private String getFullErrorStream () {
595+ StringBuilder builder = new StringBuilder ();
596+ processErrorStream (castNonNull (context .lspStreamProvider ), l -> builder .append (l + '\n' ), e -> builder .append ("Exception processing error stream: " + e )); //$NON-NLS-1$
597+ if (!builder .isEmpty ()) {
598+ return builder .toString ();
599+ }
600+ return "No errors recorded." ; //$NON-NLS-1$
601+ }
602+
603+ private void processErrorStream (StreamConnectionProvider streamProvider , Consumer <String > lineHandler , Consumer <IOException > exceptionHandler ) {
604+ var errorStream = streamProvider .getErrorStream ();
605+ if (errorStream != null ) {
606+ try (BufferedReader reader = new BufferedReader (new InputStreamReader (errorStream ))) {
607+ for (String line = reader .readLine (); line != null ; line = reader .readLine ()) {
608+ if (!line .isBlank ()) {
609+ lineHandler .accept (line );
610+ }
611+ }
612+ } catch (IOException e ) {
613+ exceptionHandler .accept (e );
614+ }
615+ }
616+ }
617+
580618 @ Nullable
581619 public ProcessHandle getProcessHandle () {
582620 return Adapters .adapt (context .lspStreamProvider , ProcessHandle .class );
0 commit comments