2323import java .util .List ;
2424import java .util .Optional ;
2525import java .util .Set ;
26+ import java .util .concurrent .Callable ;
2627import java .util .concurrent .CompletableFuture ;
2728import java .util .concurrent .ExecutionException ;
29+ import java .util .concurrent .ExecutorService ;
30+ import java .util .concurrent .Future ;
2831import java .util .concurrent .TimeUnit ;
2932import java .util .concurrent .TimeoutException ;
3033import java .util .concurrent .atomic .AtomicInteger ;
4346
4447import org .eclipse .jface .contentassist .IContentAssistSubjectControl ;
4548
49+ import org .eclipse .jface .text .Activator ;
4650import org .eclipse .jface .text .BadLocationException ;
4751import org .eclipse .jface .text .DocumentEvent ;
4852import org .eclipse .jface .text .IDocument ;
@@ -372,7 +376,7 @@ protected List<CompletableFuture<List<ICompletionProposal>>> buildCompletionFutu
372376 }
373377 List <CompletableFuture <List <ICompletionProposal >>> futures = new ArrayList <>(processors .size ());
374378 for (IContentAssistProcessor processor : processors ) {
375- futures .add (CompletableFuture . supplyAsync (() -> {
379+ futures .add (submitInterruptible (() -> {
376380 AtomicReference <List <ICompletionProposal >> result = new AtomicReference <>();
377381 SafeRunner .run (() -> {
378382 ICompletionProposal [] proposals = processor .computeCompletionProposals (fViewer , invocationOffset );
@@ -389,11 +393,42 @@ protected List<CompletableFuture<List<ICompletionProposal>>> buildCompletionFutu
389393 return Collections .emptyList ();
390394 }
391395 return proposals ;
392- }));
396+ }, Activator . getExecutor () ));
393397 }
394398 return futures ;
395399 }
396400
401+ /**
402+ * Submit a task in such a way that it actually reacts to cancellation (i.e. calls to
403+ * {@code future.cancel(true)})
404+ *
405+ * @param executor Do not use the common pool here since that one does not cancel (interrupts)
406+ * worker threads
407+ * @return an interruptible future.
408+ */
409+ private static <T > CompletableFuture <T > submitInterruptible (
410+ Callable <T > task , ExecutorService executor ) {
411+
412+ CompletableFuture <T > cf = new CompletableFuture <>();
413+
414+ Future <?> ft = executor .submit (() -> {
415+ try {
416+ cf .complete (task .call ());
417+ } catch (Exception e ) {
418+ cf .completeExceptionally (e );
419+ }
420+ });
421+
422+ // make canceling the CF also cancel the FutureTask
423+ cf .whenComplete ((r , t ) -> {
424+ if (cf .isCancelled ()) {
425+ ft .cancel (true ); // this actually interrupts
426+ }
427+ });
428+
429+ return cf ;
430+ }
431+
397432 private String getTokenContentType (int invocationOffset ) throws BadLocationException {
398433 if (fContentAssistSubjectControl != null ) {
399434 IDocument document = fContentAssistSubjectControl .getDocument ();
0 commit comments