Move out JVM debugger functionality
This commit is contained in:
@@ -7,6 +7,7 @@ plugins {
|
||||
dependencies {
|
||||
compile(kotlinStdlib())
|
||||
compileOnly(project(":kotlin-reflect-api"))
|
||||
compile(project(":compiler:psi"))
|
||||
compile(project(":core:descriptors"))
|
||||
compile(project(":core:descriptors.jvm"))
|
||||
compile(project(":compiler:frontend"))
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.core;
|
||||
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.fileTypes.FileTypeConsumer;
|
||||
import com.intellij.openapi.fileTypes.FileTypeFactory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
public class KotlinFileTypeFactory extends FileTypeFactory {
|
||||
public final static String[] KOTLIN_EXTENSIONS = new String[] { "kt", "kts" };
|
||||
private final static FileType[] KOTLIN_FILE_TYPES = new FileType[] { KotlinFileType.INSTANCE };
|
||||
public final static Set<FileType> KOTLIN_FILE_TYPES_SET = new HashSet<>(Arrays.asList(KOTLIN_FILE_TYPES));
|
||||
|
||||
@Override
|
||||
public void createFileTypes(@NotNull FileTypeConsumer consumer) {
|
||||
consumer.consume(KotlinFileType.INSTANCE, "kt;kts");
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The old package name left for compatibility reasons with Android IDE plugin
|
||||
* (it's bundled inside Android Studio).
|
||||
*/
|
||||
package org.jetbrains.kotlin.idea.completion
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
|
||||
interface CompletionInformationProvider {
|
||||
companion object {
|
||||
val EP_NAME: ExtensionPointName<CompletionInformationProvider> =
|
||||
ExtensionPointName.create("org.jetbrains.kotlin.completionInformationProvider")
|
||||
}
|
||||
|
||||
fun getContainerAndReceiverInformation(descriptor: DeclarationDescriptor): String?
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2000-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.core.surroundWith;
|
||||
|
||||
import com.intellij.lang.surroundWith.SurroundDescriptor;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.idea.core.util.CodeInsightUtils;
|
||||
import org.jetbrains.kotlin.psi.KtExpression;
|
||||
|
||||
public abstract class KotlinExpressionSurroundDescriptorBase implements SurroundDescriptor {
|
||||
@Override
|
||||
@NotNull
|
||||
public PsiElement[] getElementsToSurround(PsiFile file, int startOffset, int endOffset) {
|
||||
KtExpression expression = (KtExpression) CodeInsightUtils.findElement(
|
||||
file, startOffset, endOffset, CodeInsightUtils.ElementKind.EXPRESSION);
|
||||
|
||||
return expression == null ? PsiElement.EMPTY_ARRAY : new PsiElement[] {expression};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExclusive() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2000-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.core.surroundWith;
|
||||
|
||||
import com.intellij.lang.surroundWith.Surrounder;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils;
|
||||
import org.jetbrains.kotlin.psi.KtCallExpression;
|
||||
import org.jetbrains.kotlin.psi.KtExpression;
|
||||
import org.jetbrains.kotlin.psi.KtQualifiedExpression;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
|
||||
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isUnit;
|
||||
import static org.jetbrains.kotlin.idea.core.surroundWith.KotlinSurrounderUtils.isUsedAsStatement;
|
||||
|
||||
public abstract class KotlinExpressionSurrounder implements Surrounder {
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(@NotNull PsiElement[] elements) {
|
||||
if (elements.length != 1 || !(elements[0] instanceof KtExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
KtExpression expression = (KtExpression) elements[0];
|
||||
if (expression instanceof KtCallExpression && expression.getParent() instanceof KtQualifiedExpression) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isApplicable(expression);
|
||||
}
|
||||
|
||||
protected boolean isApplicable(@NotNull KtExpression expression) {
|
||||
BindingContext context = ResolutionUtils.analyze(expression, BodyResolveMode.PARTIAL);
|
||||
KotlinType type = context.getType(expression);
|
||||
if (type == null || (isUnit(type) && isApplicableToStatements())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isApplicableToStatements() && isUsedAsStatement(expression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean isApplicableToStatements() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public TextRange surroundElements(@NotNull Project project, @NotNull Editor editor, @NotNull PsiElement[] elements) {
|
||||
assert elements.length == 1 : "KotlinExpressionSurrounder should be applicable only for 1 expression: " + elements.length;
|
||||
return surroundExpression(project, editor, (KtExpression) elements[0]);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected abstract TextRange surroundExpression(@NotNull Project project, @NotNull Editor editor, @NotNull KtExpression expression);
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2000-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.core.surroundWith;
|
||||
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle;
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils;
|
||||
import org.jetbrains.kotlin.idea.core.util.CodeInsightUtils;
|
||||
import org.jetbrains.kotlin.psi.KtBlockExpression;
|
||||
import org.jetbrains.kotlin.psi.KtExpression;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode;
|
||||
|
||||
public class KotlinSurrounderUtils {
|
||||
public static String SURROUND_WITH = KotlinBundle.message("surround.with");
|
||||
public static String SURROUND_WITH_ERROR = KotlinBundle.message("surround.with.cannot.perform.action");
|
||||
|
||||
private KotlinSurrounderUtils() {
|
||||
}
|
||||
|
||||
public static void addStatementsInBlock(
|
||||
@NotNull KtBlockExpression block,
|
||||
@NotNull PsiElement[] statements
|
||||
) {
|
||||
PsiElement lBrace = block.getFirstChild();
|
||||
block.addRangeAfter(statements[0], statements[statements.length - 1], lBrace);
|
||||
}
|
||||
|
||||
public static void showErrorHint(@NotNull Project project, @NotNull Editor editor, @NotNull String message) {
|
||||
CodeInsightUtils.showErrorHint(project, editor, message, SURROUND_WITH, null);
|
||||
}
|
||||
|
||||
public static boolean isUsedAsStatement(@NotNull KtExpression expression) {
|
||||
BindingContext context = ResolutionUtils.analyze(expression, BodyResolveMode.PARTIAL_WITH_CFA);
|
||||
return BindingContextUtilsKt.isUsedAsStatement(expression, context);
|
||||
}
|
||||
|
||||
public static boolean isUsedAsExpression(@NotNull KtExpression expression) {
|
||||
BindingContext context = ResolutionUtils.analyze(expression, BodyResolveMode.PARTIAL_WITH_CFA);
|
||||
return BindingContextUtilsKt.isUsedAsExpression(expression, context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2000-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.core.util
|
||||
|
||||
import com.intellij.diagnostic.AttachmentFactory
|
||||
import com.intellij.openapi.diagnostic.Attachment
|
||||
import com.intellij.psi.PsiFile
|
||||
|
||||
fun attachmentByPsiFile(file: PsiFile?): Attachment? {
|
||||
if (file == null) return null
|
||||
|
||||
val virtualFile = file.virtualFile
|
||||
if (virtualFile != null) return AttachmentFactory.createAttachment(virtualFile)
|
||||
|
||||
val text = try { file.text
|
||||
} catch(e: Exception) { null }
|
||||
val name = try { file.name
|
||||
} catch(e: Exception) { null }
|
||||
|
||||
if (text != null && name != null) return Attachment(name, text)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun mergeAttachments(vararg attachments: Attachment?): Attachment {
|
||||
val builder = StringBuilder()
|
||||
attachments.forEach {
|
||||
if (it != null) {
|
||||
builder.append("----- START ${it.path} -----\n")
|
||||
builder.append(it.displayText)
|
||||
builder.append("\n----- END ${it.path} -----\n\n")
|
||||
}
|
||||
}
|
||||
|
||||
return Attachment("message.txt", builder.toString())
|
||||
}
|
||||
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
* Copyright 2000-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.core.util;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import kotlin.collections.ArraysKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind;
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils;
|
||||
import org.jetbrains.kotlin.lexer.KtTokens;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ClassQualifier;
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.Qualifier;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.*;
|
||||
|
||||
public class CodeInsightUtils {
|
||||
|
||||
@Nullable
|
||||
public static PsiElement findElement(
|
||||
@NotNull PsiFile file,
|
||||
int startOffset,
|
||||
int endOffset,
|
||||
@NotNull CodeInsightUtils.ElementKind elementKind
|
||||
) {
|
||||
Class<? extends KtElement> elementClass;
|
||||
switch (elementKind) {
|
||||
case EXPRESSION: elementClass = KtExpression.class;
|
||||
break;
|
||||
case TYPE_ELEMENT: elementClass = KtTypeElement.class;
|
||||
break;
|
||||
case TYPE_CONSTRUCTOR: elementClass = KtSimpleNameExpression.class;
|
||||
break;
|
||||
default: throw new IllegalArgumentException(elementKind.name());
|
||||
}
|
||||
PsiElement element = findElementOfClassAtRange(file, startOffset, endOffset, elementClass);
|
||||
|
||||
if (elementKind == ElementKind.TYPE_ELEMENT) return element;
|
||||
|
||||
if (elementKind == ElementKind.TYPE_CONSTRUCTOR) {
|
||||
return element != null && KtPsiUtilKt.isTypeConstructorReference(element) ? element : null;
|
||||
}
|
||||
|
||||
if (element instanceof KtScriptInitializer) {
|
||||
element = ((KtScriptInitializer) element).getBody();
|
||||
}
|
||||
|
||||
if (element == null) return null;
|
||||
|
||||
// TODO: Support binary operations in "Introduce..." refactorings
|
||||
if (element instanceof KtOperationReferenceExpression
|
||||
&& ((KtOperationReferenceExpression) element).getReferencedNameElementType() != KtTokens.IDENTIFIER
|
||||
&& element.getParent() instanceof KtBinaryExpression) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// For cases like 'this@outerClass', don't return the label part
|
||||
if (KtPsiUtil.isLabelIdentifierExpression(element)) {
|
||||
element = PsiTreeUtil.getParentOfType(element, KtExpression.class);
|
||||
}
|
||||
|
||||
if (element instanceof KtBlockExpression) {
|
||||
List<KtExpression> statements = ((KtBlockExpression) element).getStatements();
|
||||
if (statements.size() == 1) {
|
||||
KtExpression statement = statements.get(0);
|
||||
if (statement.getText().equals(element.getText())) {
|
||||
return statement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KtExpression expression = (KtExpression) element;
|
||||
|
||||
BindingContext context = ResolutionUtils.analyze(expression);
|
||||
|
||||
Qualifier qualifier = context.get(BindingContext.QUALIFIER, expression);
|
||||
if (qualifier != null) {
|
||||
if (!(qualifier instanceof ClassQualifier)) return null;
|
||||
if (((ClassQualifier) qualifier).getDescriptor().getKind() != ClassKind.OBJECT) return null;
|
||||
}
|
||||
|
||||
return expression;
|
||||
}
|
||||
|
||||
public enum ElementKind {
|
||||
EXPRESSION,
|
||||
TYPE_ELEMENT,
|
||||
TYPE_CONSTRUCTOR
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static PsiElement[] findElements(@NotNull PsiFile file, int startOffset, int endOffset, @NotNull ElementKind kind) {
|
||||
PsiElement element1 = getElementAtOffsetIgnoreWhitespaceBefore(file, startOffset);
|
||||
PsiElement element2 = getElementAtOffsetIgnoreWhitespaceAfter(file, endOffset);
|
||||
|
||||
if (element1 == null || element2 == null) return PsiElement.EMPTY_ARRAY;
|
||||
|
||||
startOffset = element1.getTextRange().getStartOffset();
|
||||
endOffset = element2.getTextRange().getEndOffset();
|
||||
|
||||
if (startOffset >= endOffset) return PsiElement.EMPTY_ARRAY;
|
||||
|
||||
PsiElement parent = PsiTreeUtil.findCommonParent(element1, element2);
|
||||
if (parent == null) return PsiElement.EMPTY_ARRAY;
|
||||
while (true) {
|
||||
if (parent instanceof KtBlockExpression) break;
|
||||
if (parent == null || parent instanceof KtFile) return PsiElement.EMPTY_ARRAY;
|
||||
parent = parent.getParent();
|
||||
}
|
||||
|
||||
element1 = getTopmostParentInside(element1, parent);
|
||||
if (startOffset != element1.getTextRange().getStartOffset()) return PsiElement.EMPTY_ARRAY;
|
||||
|
||||
element2 = getTopmostParentInside(element2, parent);
|
||||
if (endOffset != element2.getTextRange().getEndOffset()) return PsiElement.EMPTY_ARRAY;
|
||||
|
||||
List<PsiElement> array = new ArrayList<PsiElement>();
|
||||
PsiElement stopElement = element2.getNextSibling();
|
||||
for (PsiElement currentElement = element1; currentElement != stopElement; currentElement = currentElement.getNextSibling()) {
|
||||
if (!(currentElement instanceof PsiWhiteSpace)) {
|
||||
array.add(currentElement);
|
||||
}
|
||||
}
|
||||
|
||||
for (PsiElement element : array) {
|
||||
boolean correctType = kind == ElementKind.EXPRESSION && element instanceof KtExpression
|
||||
|| kind == ElementKind.TYPE_ELEMENT && element instanceof KtTypeElement
|
||||
|| kind == ElementKind.TYPE_CONSTRUCTOR && KtPsiUtilKt.isTypeConstructorReference(element);
|
||||
if (!(correctType
|
||||
|| element.getNode().getElementType() == KtTokens.SEMICOLON
|
||||
|| element instanceof PsiWhiteSpace
|
||||
|| element instanceof PsiComment)) {
|
||||
return PsiElement.EMPTY_ARRAY;
|
||||
}
|
||||
}
|
||||
|
||||
return PsiUtilCore.toPsiElementArray(array);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static <T extends PsiElement> T findElementOfClassAtRange(@NotNull PsiFile file, int startOffset, int endOffset, Class<T> aClass) {
|
||||
// When selected range is this@Fo<select>o</select> we'd like to return `@Foo`
|
||||
// But it's PSI looks like: (AT IDENTIFIER):JetLabel
|
||||
// So if we search parent starting exactly at IDENTIFIER then we find nothing
|
||||
// Solution is to retrieve label if we are on AT or IDENTIFIER
|
||||
PsiElement element1 = getParentLabelOrElement(getElementAtOffsetIgnoreWhitespaceBefore(file, startOffset));
|
||||
PsiElement element2 = getParentLabelOrElement(getElementAtOffsetIgnoreWhitespaceAfter(file, endOffset));
|
||||
|
||||
if (element1 == null || element2 == null) return null;
|
||||
|
||||
startOffset = element1.getTextRange().getStartOffset();
|
||||
endOffset = element2.getTextRange().getEndOffset();
|
||||
|
||||
T newElement = PsiTreeUtil.findElementOfClassAtRange(file, startOffset, endOffset, aClass);
|
||||
if (newElement == null ||
|
||||
newElement.getTextRange().getStartOffset() != startOffset ||
|
||||
newElement.getTextRange().getEndOffset() != endOffset) {
|
||||
return null;
|
||||
}
|
||||
return newElement;
|
||||
}
|
||||
|
||||
private static PsiElement getParentLabelOrElement(@Nullable PsiElement element) {
|
||||
if (element != null && element.getParent() instanceof KtLabelReferenceExpression) {
|
||||
return element.getParent();
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<PsiElement> findElementsOfClassInRange(@NotNull PsiFile file, int startOffset, int endOffset, Class<? extends PsiElement> ... classes) {
|
||||
PsiElement element1 = getElementAtOffsetIgnoreWhitespaceBefore(file, startOffset);
|
||||
PsiElement element2 = getElementAtOffsetIgnoreWhitespaceAfter(file, endOffset);
|
||||
|
||||
if (element1 == null || element2 == null) return Collections.emptyList();
|
||||
|
||||
startOffset = element1.getTextRange().getStartOffset();
|
||||
endOffset = element2.getTextRange().getEndOffset();
|
||||
|
||||
PsiElement parent = PsiTreeUtil.findCommonParent(element1, element2);
|
||||
if (parent == null) return Collections.emptyList();
|
||||
|
||||
element1 = getTopmostParentInside(element1, parent);
|
||||
if (startOffset != element1.getTextRange().getStartOffset()) return Collections.emptyList();
|
||||
|
||||
element2 = getTopmostParentInside(element2, parent);
|
||||
if (endOffset != element2.getTextRange().getEndOffset()) return Collections.emptyList();
|
||||
|
||||
PsiElement stopElement = element2.getNextSibling();
|
||||
List<PsiElement> result = new ArrayList<PsiElement>();
|
||||
for (PsiElement currentElement = element1; currentElement != stopElement && currentElement != null; currentElement = currentElement.getNextSibling()) {
|
||||
for (Class aClass : classes) {
|
||||
if (aClass.isInstance(currentElement)) {
|
||||
result.add(currentElement);
|
||||
}
|
||||
result.addAll(PsiTreeUtil.findChildrenOfType(currentElement, aClass));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PsiElement getTopmostParentInside(@NotNull PsiElement element, @NotNull PsiElement parent) {
|
||||
if (!parent.equals(element)) {
|
||||
while (!parent.equals(element.getParent())) {
|
||||
element = element.getParent();
|
||||
}
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiElement getElementAtOffsetIgnoreWhitespaceBefore(@NotNull PsiFile file, int offset) {
|
||||
PsiElement element = file.findElementAt(offset);
|
||||
if (element instanceof PsiWhiteSpace) {
|
||||
return file.findElementAt(element.getTextRange().getEndOffset());
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiElement getElementAtOffsetIgnoreWhitespaceAfter(@NotNull PsiFile file, int offset) {
|
||||
PsiElement element = file.findElementAt(offset - 1);
|
||||
if (element instanceof PsiWhiteSpace) {
|
||||
return file.findElementAt(element.getTextRange().getStartOffset() - 1);
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String defaultInitializer(KotlinType type) {
|
||||
if (type.isMarkedNullable()) {
|
||||
return "null";
|
||||
}
|
||||
else if (isInt(type) || isLong(type) || isShort(type) || isByte(type)) {
|
||||
return "0";
|
||||
}
|
||||
else if (isFloat(type)) {
|
||||
return "0.0f";
|
||||
}
|
||||
else if (isDouble(type)) {
|
||||
return "0.0";
|
||||
}
|
||||
else if (isChar(type)) {
|
||||
return "'\\u0000'";
|
||||
}
|
||||
else if (isBoolean(type)) {
|
||||
return "false";
|
||||
}
|
||||
else if (isUnit(type)) {
|
||||
return "Unit";
|
||||
}
|
||||
else if (isString(type)) {
|
||||
return "\"\"";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void showErrorHint(
|
||||
@NotNull Project project, @NotNull Editor editor,
|
||||
@NotNull String message, @NotNull String title,
|
||||
@Nullable String helpId
|
||||
) {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) throw new CommonRefactoringUtil.RefactoringErrorHintException(message);
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, message, title, helpId);
|
||||
}
|
||||
|
||||
private CodeInsightUtils() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Integer getStartLineOffset(@NotNull PsiFile file, int line) {
|
||||
Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
|
||||
if (document == null) return null;
|
||||
|
||||
if (line >= document.getLineCount()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int lineStartOffset = document.getLineStartOffset(line);
|
||||
return CharArrayUtil.shiftForward(document.getCharsSequence(), lineStartOffset, " \t");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Integer getEndLineOffset(@NotNull PsiFile file, int line) {
|
||||
Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
|
||||
if (document == null) return null;
|
||||
|
||||
if (line >= document.getLineCount()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int lineStartOffset = document.getLineEndOffset(line);
|
||||
return CharArrayUtil.shiftBackward(document.getCharsSequence(), lineStartOffset, " \t");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static PsiElement getTopmostElementAtOffset(@NotNull PsiElement element, int offset) {
|
||||
do {
|
||||
PsiElement parent = element.getParent();
|
||||
if (parent == null || (parent.getTextOffset() < offset) || parent instanceof KtBlockExpression) {
|
||||
break;
|
||||
}
|
||||
element = parent;
|
||||
}
|
||||
while(true);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static PsiElement getTopParentWithEndOffset(@NotNull PsiElement element, @NotNull Class<?> stopAt) {
|
||||
int endOffset = element.getTextOffset() + element.getTextLength();
|
||||
|
||||
do {
|
||||
PsiElement parent = element.getParent();
|
||||
if (parent == null || (parent.getTextOffset() + parent.getTextLength()) != endOffset) {
|
||||
break;
|
||||
}
|
||||
element = parent;
|
||||
|
||||
if (stopAt.isInstance(element)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while(true);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
public static <T> T getTopmostElementAtOffset(@NotNull PsiElement element, int offset, @NotNull Class<? extends T>... classes) {
|
||||
T lastElementOfType = null;
|
||||
if (anyIsInstance(element, classes)) {
|
||||
lastElementOfType = (T) element;
|
||||
}
|
||||
do {
|
||||
PsiElement parent = element.getParent();
|
||||
if (parent == null || (parent.getTextOffset() < offset) || parent instanceof KtBlockExpression) {
|
||||
break;
|
||||
}
|
||||
if (anyIsInstance(parent, classes)) {
|
||||
lastElementOfType = (T) parent;
|
||||
}
|
||||
element = parent;
|
||||
}
|
||||
while(true);
|
||||
|
||||
return lastElementOfType;
|
||||
}
|
||||
|
||||
private static <T> boolean anyIsInstance(PsiElement finalElement, @NotNull Class<? extends T>[] klass) {
|
||||
return ArraysKt.any(klass, aClass -> aClass.isInstance(finalElement));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2000-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.core.util
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiDirectory
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import java.io.File
|
||||
|
||||
fun File.toPsiFile(project: Project): PsiFile? = toVirtualFile()?.toPsiFile(project)
|
||||
|
||||
fun File.toVirtualFile(): VirtualFile? = LocalFileSystem.getInstance().findFileByIoFile(this)
|
||||
|
||||
fun File.toPsiDirectory(project: Project): PsiDirectory? {
|
||||
return toVirtualFile()?.let { vfile -> PsiManager.getInstance(project).findDirectory(vfile) }
|
||||
}
|
||||
|
||||
fun VirtualFile.toPsiFile(project: Project): PsiFile? = PsiManager.getInstance(project).findFile(this)
|
||||
|
||||
fun VirtualFile.toPsiDirectory(project: Project): PsiDirectory? = PsiManager.getInstance(project).findDirectory(this)
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2000-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.core.util
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.progress.util.ProgressIndicatorUtils
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
fun <T : Any> runInReadActionWithWriteActionPriorityWithPCE(f: () -> T): T =
|
||||
runInReadActionWithWriteActionPriority(f) ?: throw ProcessCanceledException()
|
||||
|
||||
fun <T : Any> runInReadActionWithWriteActionPriority(f: () -> T): T? {
|
||||
if (with(ApplicationManager.getApplication()) { isDispatchThread && isUnitTestMode }) {
|
||||
return f()
|
||||
}
|
||||
|
||||
var r: T? = null
|
||||
val complete = ProgressIndicatorUtils.runInReadActionWithWriteActionPriority {
|
||||
r = f()
|
||||
}
|
||||
|
||||
if (!complete) return null
|
||||
return r!!
|
||||
}
|
||||
|
||||
fun <T : Any> Project.runSynchronouslyWithProgress(progressTitle: String, canBeCanceled: Boolean, action: () -> T): T? {
|
||||
var result: T? = null
|
||||
ProgressManager.getInstance().runProcessWithProgressSynchronously({ result = action() }, progressTitle, canBeCanceled, this)
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2000-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.core.util
|
||||
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
|
||||
fun PsiFile.getLineStartOffset(line: Int): Int? {
|
||||
val doc = viewProvider.document ?: PsiDocumentManager.getInstance(project).getDocument(this)
|
||||
if (doc != null && line >= 0 && line < doc.lineCount) {
|
||||
val startOffset = doc.getLineStartOffset(line)
|
||||
val element = findElementAt(startOffset) ?: return startOffset
|
||||
|
||||
if (element is PsiWhiteSpace || element is PsiComment) {
|
||||
return PsiTreeUtil.skipSiblingsForward(element, PsiWhiteSpace::class.java, PsiComment::class.java)?.startOffset ?: startOffset
|
||||
}
|
||||
return startOffset
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun PsiFile.getLineEndOffset(line: Int): Int? {
|
||||
val document = viewProvider.document ?: PsiDocumentManager.getInstance(project).getDocument(this)
|
||||
return document?.getLineEndOffset(line)
|
||||
}
|
||||
|
||||
fun PsiElement.getLineNumber(start: Boolean = true): Int {
|
||||
val document = containingFile.viewProvider.document ?: PsiDocumentManager.getInstance(project).getDocument(containingFile)
|
||||
return document?.getLineNumber(if (start) this.startOffset else this.endOffset) ?: 0
|
||||
}
|
||||
|
||||
fun PsiElement.getLineCount(): Int {
|
||||
val doc = containingFile?.let { file -> PsiDocumentManager.getInstance(project).getDocument(file) }
|
||||
if (doc != null) {
|
||||
val spaceRange = textRange ?: TextRange.EMPTY_RANGE
|
||||
|
||||
if (spaceRange.endOffset <= doc.textLength) {
|
||||
val startLine = doc.getLineNumber(spaceRange.startOffset)
|
||||
val endLine = doc.getLineNumber(spaceRange.endOffset)
|
||||
|
||||
return endLine - startLine
|
||||
}
|
||||
}
|
||||
|
||||
return (text ?: "").count { it == '\n' } + 1
|
||||
}
|
||||
|
||||
fun PsiElement.isMultiLine(): Boolean = getLineCount() > 1
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2000-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.core.util
|
||||
|
||||
import com.intellij.openapi.editor.RangeMarker
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiElement
|
||||
|
||||
val TextRange.start: Int
|
||||
get() = startOffset
|
||||
|
||||
val TextRange.end: Int
|
||||
get() = endOffset
|
||||
|
||||
val PsiElement.range: TextRange
|
||||
get() = textRange!!
|
||||
|
||||
val RangeMarker.range: TextRange?
|
||||
get() = if (isValid) {
|
||||
val start = startOffset
|
||||
val end = endOffset
|
||||
if (start in 0..end) {
|
||||
TextRange(start, end)
|
||||
} else {
|
||||
// Probably a race condition had happened and range marker is invalidated
|
||||
null
|
||||
}
|
||||
} else null
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2000-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.core.util
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.JavaPsiFacade
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.idea.util.runWithAlternativeResolveEnabled
|
||||
|
||||
fun getKotlinJvmRuntimeMarkerClass(project: Project, scope: GlobalSearchScope): PsiClass? {
|
||||
return runReadAction {
|
||||
project.runWithAlternativeResolveEnabled {
|
||||
JavaPsiFacade.getInstance(project).findClass(KotlinBuiltIns.FQ_NAMES.unit.asString(), scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2000-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.core.util
|
||||
|
||||
import com.intellij.ui.DocumentAdapter
|
||||
import javax.swing.event.DocumentEvent
|
||||
import javax.swing.text.JTextComponent
|
||||
|
||||
fun JTextComponent.onTextChange(action: (DocumentEvent) -> Unit) {
|
||||
document.addDocumentListener(
|
||||
object : DocumentAdapter() {
|
||||
override fun textChanged(e: DocumentEvent) {
|
||||
action(e)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user