// returns (injected psi, leaf element at the offset, language of the leaf element) // since findElementAt() is expensive, we trying to reuse its result @NotNull private static Trinity<PsiElement, PsiElement, Language> tryOffset( @NotNull PsiFile hostFile, final int offset, @NotNull PsiDocumentManager documentManager) { FileViewProvider provider = hostFile.getViewProvider(); Language leafLanguage = null; PsiElement leafElement = null; for (Language language : provider.getLanguages()) { PsiElement element = provider.findElementAt(offset, language); if (element != null) { if (leafLanguage == null) { leafLanguage = language; leafElement = element; } PsiElement injected = findInside(element, hostFile, offset, documentManager); if (injected != null) return Trinity.create(injected, element, language); } // maybe we are at the border between two psi elements, then try to find injection at the end // of the left element if (offset != 0 && (element == null || element.getTextRange().getStartOffset() == offset)) { PsiElement leftElement = provider.findElementAt(offset - 1, language); if (leftElement != null && leftElement.getTextRange().getEndOffset() == offset) { PsiElement injected = findInside(leftElement, hostFile, offset, documentManager); if (injected != null) return Trinity.create(injected, element, language); } } } return Trinity.create(null, leafElement, leafLanguage); }
@Nullable( "null means no luck, otherwise it's tuple(guessed encoding, hint about content if was unable to guess, BOM)") public static Trinity<Charset, CharsetToolkit.GuessedEncoding, byte[]> guessFromContent( @NotNull VirtualFile virtualFile, @NotNull byte[] content, int length) { Charset defaultCharset = ObjectUtils.notNull( EncodingManager.getInstance().getEncoding(virtualFile, true), CharsetToolkit.getDefaultSystemCharset()); CharsetToolkit toolkit = GUESS_UTF ? new CharsetToolkit(content, defaultCharset) : null; String detectedFromBytes = null; try { if (GUESS_UTF) { toolkit.setEnforce8Bit(true); Charset charset = toolkit.guessFromBOM(); if (charset != null) { detectedFromBytes = AUTO_DETECTED_FROM_BOM; byte[] bom = ObjectUtils.notNull(CharsetToolkit.getMandatoryBom(charset), CharsetToolkit.UTF8_BOM); return Trinity.create(charset, null, bom); } CharsetToolkit.GuessedEncoding guessed = toolkit.guessFromContent(length); if (guessed == CharsetToolkit.GuessedEncoding.VALID_UTF8) { detectedFromBytes = "auto-detected from bytes"; return Trinity.create( CharsetToolkit.UTF8_CHARSET, guessed, null); // UTF detected, ignore all directives } if (guessed == CharsetToolkit.GuessedEncoding.SEVEN_BIT) { return Trinity.create(null, guessed, null); } } return null; } finally { setCharsetWasDetectedFromBytes(virtualFile, detectedFromBytes); } }
private static PsiFile createFileCopy(PsiFile file, long caret, long selEnd) { final VirtualFile virtualFile = file.getVirtualFile(); boolean mayCacheCopy = file.isPhysical() && // we don't want to cache code fragment copies even if they appear to be physical virtualFile != null && virtualFile.isInLocalFileSystem(); long combinedOffsets = caret + (selEnd << 32); if (mayCacheCopy) { final Trinity<PsiFile, Document, Long> cached = SoftReference.dereference(file.getUserData(FILE_COPY_KEY)); if (cached != null && cached.first.getClass().equals(file.getClass()) && isCopyUpToDate(cached.second, cached.first)) { final PsiFile copy = cached.first; if (copy.getViewProvider().getModificationStamp() > file.getViewProvider().getModificationStamp() && cached.third.longValue() != combinedOffsets) { // the copy PSI might have some caches that are not cleared on its modification because // there are no events in the copy // so, clear all the caches // hopefully it's a rare situation that the user invokes completion in different parts of // the file // without modifying anything physical in between ((PsiModificationTrackerImpl) file.getManager().getModificationTracker()).incCounter(); } final Document document = cached.second; assert document != null; file.putUserData( FILE_COPY_KEY, new SoftReference<Trinity<PsiFile, Document, Long>>( Trinity.create(copy, document, combinedOffsets))); Document originalDocument = file.getViewProvider().getDocument(); assert originalDocument != null; assert originalDocument.getTextLength() == file.getTextLength() : originalDocument; document.setText(originalDocument.getImmutableCharSequence()); return copy; } } final PsiFile copy = (PsiFile) file.copy(); if (mayCacheCopy) { final Document document = copy.getViewProvider().getDocument(); assert document != null; file.putUserData( FILE_COPY_KEY, new SoftReference<Trinity<PsiFile, Document, Long>>( Trinity.create(copy, document, combinedOffsets))); } return copy; }
// todo [roma] regexp @Nullable static Trinity<TextRange, TextRange, TextRange> parseExceptionLine(final String line) { int startIdx; if (line.startsWith(AT_PREFIX)) { startIdx = 0; } else { startIdx = line.indexOf(STANDALONE_AT); if (startIdx < 0) { startIdx = line.indexOf(AT_PREFIX); } if (startIdx < 0) { startIdx = -1; } } final int lparenIdx = line.indexOf('(', startIdx); if (lparenIdx < 0) return null; final int dotIdx = line.lastIndexOf('.', lparenIdx); if (dotIdx < 0 || dotIdx < startIdx) return null; final int rparenIdx = line.indexOf(')', lparenIdx); if (rparenIdx < 0) return null; // class, method, link return Trinity.create( new TextRange( startIdx + 1 + (startIdx >= 0 ? AT.length() : 0), handleSpaces(line, dotIdx, -1, true)), new TextRange( handleSpaces(line, dotIdx + 1, 1, true), handleSpaces(line, lparenIdx + 1, -1, true)), new TextRange(lparenIdx, rparenIdx)); }
private void startListening( @NotNull final PsiModifierListOwner expectedOwner, @NotNull final String expectedAnnotationFQName, final boolean expectedSuccessful) { startListening( Arrays.asList(Trinity.create(expectedOwner, expectedAnnotationFQName, expectedSuccessful))); }
@NotNull private Trinity<String[], VirtualFile[], VirtualFile[]> cacheThings() { Trinity<String[], VirtualFile[], VirtualFile[]> result; if (myList.isEmpty()) { result = EMPTY; } else { VirtualFilePointer[] vf = myList.toArray(new VirtualFilePointer[myList.size()]); List<VirtualFile> cachedFiles = new ArrayList<VirtualFile>(vf.length); List<String> cachedUrls = new ArrayList<String>(vf.length); List<VirtualFile> cachedDirectories = new ArrayList<VirtualFile>(vf.length / 3); boolean allFilesAreDirs = true; for (VirtualFilePointer v : vf) { VirtualFile file = v.getFile(); String url = v.getUrl(); cachedUrls.add(url); if (file != null) { cachedFiles.add(file); if (file.isDirectory()) { cachedDirectories.add(file); } else { allFilesAreDirs = false; } } } VirtualFile[] directories = VfsUtilCore.toVirtualFileArray(cachedDirectories); VirtualFile[] files = allFilesAreDirs ? directories : VfsUtilCore.toVirtualFileArray(cachedFiles); String[] urlsArray = ArrayUtil.toStringArray(cachedUrls); result = Trinity.create(urlsArray, files, directories); } myCachedThings = result; myTimeStampOfCachedThings = myVirtualFilePointerManager.getModificationCount(); return result; }
public void initMarkers(Place shreds) { SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(myProject); int curOffset = -1; for (PsiLanguageInjectionHost.Shred shred : shreds) { final RangeMarker rangeMarker = myNewDocument.createRangeMarker( shred.getRange().getStartOffset() + shred.getPrefix().length(), shred.getRange().getEndOffset() - shred.getSuffix().length()); final TextRange rangeInsideHost = shred.getRangeInsideHost(); PsiLanguageInjectionHost host = shred.getHost(); RangeMarker origMarker = myOrigDocument.createRangeMarker( rangeInsideHost.shiftRight(host.getTextRange().getStartOffset())); SmartPsiElementPointer<PsiLanguageInjectionHost> elementPointer = smartPointerManager.createSmartPsiElementPointer(host); Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer> markers = Trinity.<RangeMarker, RangeMarker, SmartPsiElementPointer>create( origMarker, rangeMarker, elementPointer); myMarkers.add(markers); origMarker.setGreedyToRight(true); rangeMarker.setGreedyToRight(true); if (origMarker.getStartOffset() > curOffset) { origMarker.setGreedyToLeft(true); rangeMarker.setGreedyToLeft(true); } curOffset = origMarker.getEndOffset(); } initGuardedBlocks(shreds); }
public Trinity<List<LookupElement>, Iterable<List<LookupElement>>, Boolean> getModelSnapshot() { synchronized (lock) { final List<LookupElement> sorted = new ArrayList<LookupElement>(mySortedItems); final Iterable<List<LookupElement>> groups = myRelevanceClassifier.classify(sorted); boolean changed = lastAccess != stamp; lastAccess = stamp; return Trinity.create(sorted, groups, changed); } }
public void testAnnotateLibrary() throws Throwable { addDefaultLibrary(); myFixture.configureByFiles("lib/p/TestPrimitive.java", "content/anno/p/annotations.xml"); myFixture.configureByFiles("lib/p/Test.java"); final PsiFile file = myFixture.getFile(); final Editor editor = myFixture.getEditor(); final IntentionAction fix = myFixture.findSingleIntention("Annotate method 'get' as @NotNull"); assertTrue(fix.isAvailable(myProject, editor, file)); // expecting other @Nullable annotations to be removed, and default @NotNull to be added List<Trinity<PsiModifierListOwner, String, Boolean>> expectedSequence = new ArrayList<Trinity<PsiModifierListOwner, String, Boolean>>(); for (String notNull : NullableNotNullManager.getInstance(myProject).getNullables()) { expectedSequence.add(Trinity.create(getOwner(), notNull, false)); } expectedSequence.add(Trinity.create(getOwner(), AnnotationUtil.NOT_NULL, true)); startListening(expectedSequence); new WriteCommandAction(myProject) { @Override protected void run(final Result result) throws Throwable { fix.invoke(myProject, editor, file); } }.execute(); FileDocumentManager.getInstance().saveAllDocuments(); final PsiElement psiElement = file.findElementAt(editor.getCaretModel().getOffset()); assertNotNull(psiElement); final PsiModifierListOwner listOwner = PsiTreeUtil.getParentOfType(psiElement, PsiModifierListOwner.class); assertNotNull(listOwner); assertNotNull( ExternalAnnotationsManager.getInstance(myProject) .findExternalAnnotation(listOwner, AnnotationUtil.NOT_NULL)); stopListeningAndCheckEvents(); myFixture.checkResultByFile( "content/anno/p/annotations.xml", "content/anno/p/annotationsAnnotateLibrary_after.xml", false); }
@NotNull private GroovyResolveResult[] doPolyResolveWithCaching( final boolean incompleteCode, final boolean genericsMatter) { final InferenceContext context = TypeInferenceHelper.getCurrentContext(); final Trinity<?, ?, ?> key = Trinity.create(this, context, Pair.create(incompleteCode, genericsMatter)); final GroovyResolveResult[] value = RecursionManager.doPreventingRecursion( key, true, new Computable<GroovyResolveResult[]>() { @Override public GroovyResolveResult[] compute() { return doPolyResolve(incompleteCode, genericsMatter); } }); return value == null ? GroovyResolveResult.EMPTY_ARRAY : value; }
@NotNull private Pair<Boolean, GroovyResolveResult[]> resolveByShape( final boolean allVariants, @Nullable final GrExpression upToArgument) { LOG.assertTrue(allVariants || upToArgument == null); final Trinity key = Trinity.create( TypeInferenceHelper.getCurrentContext(), this, Pair.create(allVariants, upToArgument)); final Pair<Boolean, GroovyResolveResult[]> result = RecursionManager.doPreventingRecursion( key, true, new Computable<Pair<Boolean, GroovyResolveResult[]>>() { @Override public Pair<Boolean, GroovyResolveResult[]> compute() { return doResolveByShape(allVariants, upToArgument); } }); return result == null ? Pair.create(false, GroovyResolveResult.EMPTY_ARRAY) : result; }
@Nullable private <TRef extends PsiReference, TResult> TResult resolve( @NotNull final TRef ref, @NotNull final AbstractResolver<TRef, TResult> resolver, boolean needToPreventRecursion, final boolean incompleteCode, boolean isPoly, boolean isPhysical) { ProgressIndicatorProvider.checkCanceled(); if (isPhysical) { ApplicationManager.getApplication().assertReadAccessAllowed(); } int index = getIndex(isPhysical, incompleteCode, isPoly); ConcurrentMap<TRef, TResult> map = getMap(index); TResult result = map.get(ref); if (result != null) { return result; } RecursionGuard.StackStamp stamp = myGuard.markStack(); result = needToPreventRecursion ? myGuard.doPreventingRecursion( Trinity.create(ref, incompleteCode, isPoly), true, new Computable<TResult>() { @Override public TResult compute() { return resolver.resolve(ref, incompleteCode); } }) : resolver.resolve(ref, incompleteCode); PsiElement element = result instanceof ResolveResult ? ((ResolveResult) result).getElement() : null; LOG.assertTrue(element == null || element.isValid(), result); if (stamp.mayCacheNow()) { cache(ref, map, result); } return result; }
@Override public String toString() { StringBuilder sb = new StringBuilder(); if (myVarIndex.size() != myVarSizes.size()) { return "inconsistent"; } List<Trinity<DeclarationDescriptor, Integer, Integer>> descriptors = Lists.newArrayList(); for (Object descriptor0 : myVarIndex.keys()) { DeclarationDescriptor descriptor = (DeclarationDescriptor) descriptor0; int varIndex = myVarIndex.get(descriptor); int varSize = myVarSizes.get(descriptor); descriptors.add(Trinity.create(descriptor, varIndex, varSize)); } Collections.sort( descriptors, new Comparator<Trinity<DeclarationDescriptor, Integer, Integer>>() { @Override public int compare( Trinity<DeclarationDescriptor, Integer, Integer> left, Trinity<DeclarationDescriptor, Integer, Integer> right) { return left.second - right.second; } }); sb.append("size=").append(myMaxIndex); boolean first = true; for (Trinity<DeclarationDescriptor, Integer, Integer> t : descriptors) { if (!first) { sb.append(", "); } first = false; sb.append(t.first).append(",i=").append(t.second).append(",s=").append(t.third); } return sb.toString(); }
@NotNull public DfaVariableValue createVariableValue( @NotNull PsiModifierListOwner myVariable, @Nullable PsiType varType, boolean isNegated, @Nullable DfaVariableValue qualifier) { Trinity<Boolean, String, DfaVariableValue> key = Trinity.create(isNegated, ((PsiNamedElement) myVariable).getName(), qualifier); for (DfaVariableValue aVar : myExistingVars.get(key)) { if (aVar.hardEquals(myVariable, varType, isNegated, qualifier)) return aVar; } DfaVariableValue result = new DfaVariableValue(myVariable, varType, isNegated, myFactory, qualifier); myExistingVars.putValue(key, result); while (qualifier != null) { qualifier.myDependents.add(result); qualifier = qualifier.getQualifier(); } return result; }
private static PsiFile insertDummyIdentifierIfNeeded( PsiFile file, final int startOffset, final int endOffset, String replacement) { Document originalDocument = file.getViewProvider().getDocument(); assert originalDocument != null; if (replacement.isEmpty() && PsiDocumentManager.getInstance(file.getProject()).isCommitted(originalDocument)) { return file; } ConcurrentFactoryMap<Trinity<Integer, Integer, String>, PsiFile> map = CachedValuesManager.getCachedValue( file, () -> CachedValueProvider.Result.create( new ConcurrentFactoryMap<Trinity<Integer, Integer, String>, PsiFile>() { @Nullable @Override protected PsiFile create(Trinity<Integer, Integer, String> key) { PsiFile copy = (PsiFile) file.copy(); final Document document = copy.getViewProvider().getDocument(); assert document != null; document.setText( originalDocument .getImmutableCharSequence()); // original file might be uncommitted document.replaceString(key.first, key.second, key.third); PsiDocumentManager.getInstance(copy.getProject()).commitDocument(document); return copy; } }, file, originalDocument)); return map.get(Trinity.create(startOffset, endOffset, replacement)); }
private static Trinity<PsiElement, TextRange, Value> getSelectedExpression( final Project project, final Editor editor, final Point point, final ValueHintType type) { final Ref<PsiElement> selectedExpression = Ref.create(null); final Ref<TextRange> currentRange = Ref.create(null); final Ref<Value> preCalculatedValue = Ref.create(null); PsiDocumentManager.getInstance(project) .commitAndRunReadAction( new Runnable() { public void run() { // Point -> offset final int offset = calculateOffset(editor, point); PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()); if (psiFile == null || !psiFile.isValid()) return; int selectionStart = editor.getSelectionModel().getSelectionStart(); int selectionEnd = editor.getSelectionModel().getSelectionEnd(); if ((type == ValueHintType.MOUSE_CLICK_HINT || type == ValueHintType.MOUSE_ALT_OVER_HINT) && (selectionStart <= offset && offset <= selectionEnd)) { PsiElement ctx = (selectionStart > 0) ? psiFile.findElementAt(selectionStart - 1) : psiFile.findElementAt(selectionStart); try { String text = editor.getSelectionModel().getSelectedText(); if (text != null && ctx != null) { selectedExpression.set( JVMElementFactories.getFactory(ctx.getLanguage(), project) .createExpressionFromText(text, ctx)); currentRange.set( new TextRange( editor.getSelectionModel().getSelectionStart(), editor.getSelectionModel().getSelectionEnd())); } } catch (IncorrectOperationException ignored) { } } if (currentRange.get() == null) { PsiElement elementAtCursor = psiFile.findElementAt(offset); if (elementAtCursor == null) { return; } Pair<PsiElement, TextRange> pair = findExpression( elementAtCursor, type == ValueHintType.MOUSE_CLICK_HINT || type == ValueHintType.MOUSE_ALT_OVER_HINT); if (pair == null) { if (type == ValueHintType.MOUSE_OVER_HINT) { final DebuggerSession debuggerSession = DebuggerManagerEx.getInstanceEx(project) .getContext() .getDebuggerSession(); if (debuggerSession != null && debuggerSession.isPaused()) { final Pair<Method, Value> lastExecuted = debuggerSession.getProcess().getLastExecutedMethod(); if (lastExecuted != null) { final Method method = lastExecuted.getFirst(); if (method != null) { final Pair<PsiElement, TextRange> expressionPair = findExpression(elementAtCursor, true); if (expressionPair != null && expressionPair.getFirst() instanceof PsiMethodCallExpression) { final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression) expressionPair.getFirst(); final PsiMethod psiMethod = methodCallExpression.resolveMethod(); if (psiMethod != null) { final JVMName jvmSignature = JVMNameUtil.getJVMSignature(psiMethod); try { if (method.name().equals(psiMethod.getName()) && method .signature() .equals( jvmSignature.getName(debuggerSession.getProcess()))) { pair = expressionPair; preCalculatedValue.set(lastExecuted.getSecond()); } } catch (EvaluateException ignored) { } } } } } } } } if (pair == null) { return; } selectedExpression.set(pair.getFirst()); currentRange.set(pair.getSecond()); } } }); return Trinity.create(selectedExpression.get(), currentRange.get(), preCalculatedValue.get()); }
@Override public void addMessage(PsiElement context, String message, @NotNull ErrorType type) { messages.add(Trinity.create(context, message, type)); }
/** @author dsl */ public class VirtualFilePointerContainerImpl extends TraceableDisposable implements VirtualFilePointerContainer, Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.pointers.VirtualFilePointerContainer"); @NotNull private final List<VirtualFilePointer> myList = ContainerUtil.createLockFreeCopyOnWriteList(); @NotNull private final VirtualFilePointerManager myVirtualFilePointerManager; @NotNull private final Disposable myParent; private final VirtualFilePointerListener myListener; private volatile Trinity<String[], VirtualFile[], VirtualFile[]> myCachedThings; private volatile long myTimeStampOfCachedThings = -1; @NonNls public static final String URL_ATTR = "url"; private boolean myDisposed; private static final boolean TRACE_CREATION = LOG.isDebugEnabled() || ApplicationManager.getApplication().isUnitTestMode(); public VirtualFilePointerContainerImpl( @NotNull VirtualFilePointerManager manager, @NotNull Disposable parentDisposable, @Nullable VirtualFilePointerListener listener) { //noinspection HardCodedStringLiteral super( TRACE_CREATION && !ApplicationInfoImpl.isInPerformanceTest() ? new Throwable( "parent = '" + parentDisposable + "' (" + parentDisposable.getClass() + "); listener=" + listener) : null); myVirtualFilePointerManager = manager; myParent = parentDisposable; myListener = listener; } @Override public void readExternal(@NotNull final Element rootChild, @NotNull final String childElements) throws InvalidDataException { final List urls = rootChild.getChildren(childElements); for (Object url : urls) { Element pathElement = (Element) url; final String urlAttribute = pathElement.getAttributeValue(URL_ATTR); if (urlAttribute == null) throw new InvalidDataException("path element without url"); add(urlAttribute); } } @Override public void writeExternal( @NotNull final Element element, @NotNull final String childElementName) { for (VirtualFilePointer pointer : myList) { String url = pointer.getUrl(); final Element rootPathElement = new Element(childElementName); rootPathElement.setAttribute(URL_ATTR, url); element.addContent(rootPathElement); } } @Override public void moveUp(@NotNull String url) { int index = indexOf(url); if (index <= 0) return; dropCaches(); ContainerUtil.swapElements(myList, index - 1, index); } @Override public void moveDown(@NotNull String url) { int index = indexOf(url); if (index < 0 || index + 1 >= myList.size()) return; dropCaches(); ContainerUtil.swapElements(myList, index, index + 1); } private int indexOf(@NotNull final String url) { for (int i = 0; i < myList.size(); i++) { final VirtualFilePointer pointer = myList.get(i); if (url.equals(pointer.getUrl())) { return i; } } return -1; } @Override public void killAll() { myList.clear(); } @Override public void add(@NotNull VirtualFile file) { assert !myDisposed; dropCaches(); final VirtualFilePointer pointer = create(file); myList.add(pointer); } @Override public void add(@NotNull String url) { assert !myDisposed; dropCaches(); final VirtualFilePointer pointer = create(url); myList.add(pointer); } @Override public void remove(@NotNull VirtualFilePointer pointer) { assert !myDisposed; dropCaches(); final boolean result = myList.remove(pointer); LOG.assertTrue(result); } @Override @NotNull public List<VirtualFilePointer> getList() { assert !myDisposed; return Collections.unmodifiableList(myList); } @Override public void addAll(@NotNull VirtualFilePointerContainer that) { assert !myDisposed; dropCaches(); List<VirtualFilePointer> thatList = ((VirtualFilePointerContainerImpl) that).myList; for (final VirtualFilePointer pointer : thatList) { myList.add(duplicate(pointer)); } } private void dropCaches() { myTimeStampOfCachedThings = -1; // make it never equal to myVirtualFilePointerManager.getModificationCount() myCachedThings = EMPTY; } @Override @NotNull public String[] getUrls() { return getOrCache().first; } @NotNull private Trinity<String[], VirtualFile[], VirtualFile[]> getOrCache() { assert !myDisposed; long timeStamp = myTimeStampOfCachedThings; Trinity<String[], VirtualFile[], VirtualFile[]> cached = myCachedThings; return timeStamp == myVirtualFilePointerManager.getModificationCount() ? cached : cacheThings(); } private static final Trinity<String[], VirtualFile[], VirtualFile[]> EMPTY = Trinity.create( ArrayUtil.EMPTY_STRING_ARRAY, VirtualFile.EMPTY_ARRAY, VirtualFile.EMPTY_ARRAY); @NotNull private Trinity<String[], VirtualFile[], VirtualFile[]> cacheThings() { Trinity<String[], VirtualFile[], VirtualFile[]> result; if (myList.isEmpty()) { result = EMPTY; } else { VirtualFilePointer[] vf = myList.toArray(new VirtualFilePointer[myList.size()]); List<VirtualFile> cachedFiles = new ArrayList<VirtualFile>(vf.length); List<String> cachedUrls = new ArrayList<String>(vf.length); List<VirtualFile> cachedDirectories = new ArrayList<VirtualFile>(vf.length / 3); boolean allFilesAreDirs = true; for (VirtualFilePointer v : vf) { VirtualFile file = v.getFile(); String url = v.getUrl(); cachedUrls.add(url); if (file != null) { cachedFiles.add(file); if (file.isDirectory()) { cachedDirectories.add(file); } else { allFilesAreDirs = false; } } } VirtualFile[] directories = VfsUtilCore.toVirtualFileArray(cachedDirectories); VirtualFile[] files = allFilesAreDirs ? directories : VfsUtilCore.toVirtualFileArray(cachedFiles); String[] urlsArray = ArrayUtil.toStringArray(cachedUrls); result = Trinity.create(urlsArray, files, directories); } myCachedThings = result; myTimeStampOfCachedThings = myVirtualFilePointerManager.getModificationCount(); return result; } @Override @NotNull public VirtualFile[] getFiles() { return getOrCache().second; } @Override @NotNull public VirtualFile[] getDirectories() { return getOrCache().third; } @Override @Nullable public VirtualFilePointer findByUrl(@NotNull String url) { assert !myDisposed; for (VirtualFilePointer pointer : myList) { if (url.equals(pointer.getUrl())) return pointer; } return null; } @Override public void clear() { dropCaches(); killAll(); } @Override public int size() { return myList.size(); } public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof VirtualFilePointerContainerImpl)) return false; final VirtualFilePointerContainerImpl virtualFilePointerContainer = (VirtualFilePointerContainerImpl) o; return myList.equals(virtualFilePointerContainer.myList); } public int hashCode() { return myList.hashCode(); } protected VirtualFilePointer create(@NotNull VirtualFile file) { return myVirtualFilePointerManager.create(file, myParent, myListener); } protected VirtualFilePointer create(@NotNull String url) { return myVirtualFilePointerManager.create(url, myParent, myListener); } protected VirtualFilePointer duplicate(@NotNull VirtualFilePointer virtualFilePointer) { return myVirtualFilePointerManager.duplicate(virtualFilePointer, myParent, myListener); } @NotNull @NonNls @Override public String toString() { return "VFPContainer: " + myList /*+"; parent:"+myParent*/; } @Override @NotNull public VirtualFilePointerContainer clone(@NotNull Disposable parent) { return clone(parent, null); } @Override @NotNull public VirtualFilePointerContainer clone( @NotNull Disposable parent, @Nullable VirtualFilePointerListener listener) { assert !myDisposed; VirtualFilePointerContainer clone = myVirtualFilePointerManager.createContainer(parent, listener); for (VirtualFilePointer pointer : myList) { clone.add(pointer.getUrl()); } return clone; } @Override public void dispose() { assert !myDisposed; myDisposed = true; kill(null); } }
@Override public void startRunProfile( @NotNull final RunProfileStarter starter, @NotNull final RunProfileState state, @NotNull final ExecutionEnvironment environment) { final Project project = environment.getProject(); RunContentDescriptor reuseContent = getContentManager().getReuseContent(environment); if (reuseContent != null) { reuseContent.setExecutionId(environment.getExecutionId()); environment.setContentToReuse(reuseContent); } final Executor executor = environment.getExecutor(); project .getMessageBus() .syncPublisher(EXECUTION_TOPIC) .processStartScheduled(executor.getId(), environment); Runnable startRunnable; startRunnable = () -> { if (project.isDisposed()) { return; } RunProfile profile = environment.getRunProfile(); boolean started = false; try { project .getMessageBus() .syncPublisher(EXECUTION_TOPIC) .processStarting(executor.getId(), environment); final RunContentDescriptor descriptor = starter.execute(state, environment); if (descriptor != null) { final Trinity<RunContentDescriptor, RunnerAndConfigurationSettings, Executor> trinity = Trinity.create( descriptor, environment.getRunnerAndConfigurationSettings(), executor); myRunningConfigurations.add(trinity); Disposer.register(descriptor, () -> myRunningConfigurations.remove(trinity)); getContentManager() .showRunContent(executor, descriptor, environment.getContentToReuse()); final ProcessHandler processHandler = descriptor.getProcessHandler(); if (processHandler != null) { if (!processHandler.isStartNotified()) { processHandler.startNotify(); } project .getMessageBus() .syncPublisher(EXECUTION_TOPIC) .processStarted(executor.getId(), environment, processHandler); started = true; ProcessExecutionListener listener = new ProcessExecutionListener( project, executor.getId(), environment, processHandler, descriptor); processHandler.addProcessListener(listener); // Since we cannot guarantee that the listener is added before process handled is // start notified, // we have to make sure the process termination events are delivered to the clients. // Here we check the current process state and manually deliver events, while // the ProcessExecutionListener guarantees each such event is only delivered once // either by this code, or by the ProcessHandler. boolean terminating = processHandler.isProcessTerminating(); boolean terminated = processHandler.isProcessTerminated(); if (terminating || terminated) { listener.processWillTerminate( new ProcessEvent(processHandler), false /*doesn't matter*/); if (terminated) { //noinspection ConstantConditions int exitCode = processHandler.getExitCode(); listener.processTerminated(new ProcessEvent(processHandler, exitCode)); } } } environment.setContentToReuse(descriptor); } } catch (ProcessCanceledException e) { LOG.info(e); } catch (ExecutionException e) { ExecutionUtil.handleExecutionError(project, executor.getToolWindowId(), profile, e); LOG.info(e); } finally { if (!started) { project .getMessageBus() .syncPublisher(EXECUTION_TOPIC) .processNotStarted(executor.getId(), environment); } } }; if (ApplicationManager.getApplication().isUnitTestMode() && !myForceCompilationInTests) { startRunnable.run(); } else { compileAndRun( () -> TransactionGuard.submitTransaction(project, startRunnable), environment, state, () -> { if (!project.isDisposed()) { project .getMessageBus() .syncPublisher(EXECUTION_TOPIC) .processNotStarted(executor.getId(), environment); } }); } }