void (@NotNull PsiElement element, @NotNull CharSequence prefix, @NotNull Consumer<? super Consumer<Object>> provider) { Project project = element.getProject(); StringBuilder sb = new StringBuilder(prefix); ConcurrentLinkedQueue<Object> queue = new ConcurrentLinkedQueue<>(); Disposable alarmDisposable = Disposer.newDisposable(); Disposer.register(project, alarmDisposable); AtomicBoolean stop = new AtomicBoolean(false); Ref<Object> cutAt = Ref.create(null); SingleAlarm alarm = new SingleAlarm(() -> { DocumentationComponent component = getActiveDocComponent(project); if (component == null) { stop.set(true); Disposer.dispose(alarmDisposable); return; } Object s = queue.poll(); while (s != null) { if (s == CUT_AT_CMD || cutAt.get() == CUT_AT_CMD) { cutAt.set(s); s = ""; } else if (!cutAt.isNull()) { int idx = StringUtil.indexOf(sb, cutAt.get().toString()); if (idx >= 0) sb.setLength(idx); cutAt.set(null); } sb.append(s); s = queue.poll(); } if (stop.get()) { Disposer.dispose(alarmDisposable); } String newText = sb + "<br><br><br>"; String prevText = component.getText(); if (!Objects.equals(newText, prevText)) { component.replaceText(newText, element); } }, 100, alarmDisposable); ApplicationManager.getApplication().executeOnPooledThread(() -> { try { provider.consume((Consumer<Object>)str -> { ProgressManager.checkCanceled(); if (stop.get()) throw new ProcessCanceledException(); queue.add(str); alarm.cancelAndRequest(); }); } finally { if (stop.compareAndSet(false, true)) { alarm.cancelAndRequest(); } } }); }