mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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 com.intellij.diff.impl;
|
||||
|
||||
import com.intellij.diff.util.DiffPlaces;
|
||||
import com.intellij.diff.util.DiffUtil;
|
||||
import com.intellij.openapi.components.PersistentStateComponent;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import com.intellij.util.xmlb.annotations.MapAnnotation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@State(
|
||||
name = "DiffSettings",
|
||||
storages = @Storage(DiffUtil.DIFF_CONFIG)
|
||||
)
|
||||
public class DiffSettingsHolder implements PersistentStateComponent<DiffSettingsHolder.State> {
|
||||
public static final Key<DiffSettings> KEY = Key.create("DiffSettings");
|
||||
|
||||
private static class SharedSettings {
|
||||
public boolean GO_TO_NEXT_FILE_ON_NEXT_DIFFERENCE = true;
|
||||
}
|
||||
|
||||
private static class PlaceSettings {
|
||||
@NotNull public List<String> DIFF_TOOLS_ORDER = new ArrayList<>();
|
||||
public boolean SYNC_BINARY_EDITOR_SETTINGS = true;
|
||||
}
|
||||
|
||||
public static class DiffSettings {
|
||||
@NotNull public SharedSettings SHARED_SETTINGS = new SharedSettings();
|
||||
@NotNull public PlaceSettings PLACE_SETTINGS = new PlaceSettings();
|
||||
|
||||
public DiffSettings() {
|
||||
}
|
||||
|
||||
public DiffSettings(@NotNull SharedSettings SHARED_SETTINGS,
|
||||
@NotNull PlaceSettings PLACE_SETTINGS) {
|
||||
this.SHARED_SETTINGS = SHARED_SETTINGS;
|
||||
this.PLACE_SETTINGS = PLACE_SETTINGS;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<String> getDiffToolsOrder() {
|
||||
return PLACE_SETTINGS.DIFF_TOOLS_ORDER;
|
||||
}
|
||||
|
||||
public void setDiffToolsOrder(@NotNull List<String> order) {
|
||||
PLACE_SETTINGS.DIFF_TOOLS_ORDER = order;
|
||||
}
|
||||
|
||||
public boolean isGoToNextFileOnNextDifference() {
|
||||
return SHARED_SETTINGS.GO_TO_NEXT_FILE_ON_NEXT_DIFFERENCE;
|
||||
}
|
||||
|
||||
public void setGoToNextFileOnNextDifference(boolean value) {
|
||||
SHARED_SETTINGS.GO_TO_NEXT_FILE_ON_NEXT_DIFFERENCE = value;
|
||||
}
|
||||
|
||||
public boolean isSyncBinaryEditorSettings() {
|
||||
return PLACE_SETTINGS.SYNC_BINARY_EDITOR_SETTINGS;
|
||||
}
|
||||
|
||||
public void setSyncBinaryEditorSettings(boolean value) {
|
||||
PLACE_SETTINGS.SYNC_BINARY_EDITOR_SETTINGS= value;
|
||||
}
|
||||
|
||||
//
|
||||
// Impl
|
||||
//
|
||||
|
||||
@NotNull
|
||||
public static DiffSettings getSettings() {
|
||||
return getSettings(null);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static DiffSettings getSettings(@Nullable String place) {
|
||||
return getInstance().getSettings(place);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DiffSettings getSettings(@Nullable String place) {
|
||||
if (place == null) place = DiffPlaces.DEFAULT;
|
||||
|
||||
PlaceSettings placeSettings = myState.PLACES_MAP.get(place);
|
||||
if (placeSettings == null) {
|
||||
placeSettings = new PlaceSettings();
|
||||
myState.PLACES_MAP.put(place, placeSettings);
|
||||
}
|
||||
return new DiffSettings(myState.SHARED_SETTINGS, placeSettings);
|
||||
}
|
||||
|
||||
public static class State {
|
||||
@MapAnnotation(surroundWithTag = false, surroundKeyWithTag = false, surroundValueWithTag = false)
|
||||
public Map<String, PlaceSettings> PLACES_MAP = new HashMap<>();
|
||||
public SharedSettings SHARED_SETTINGS = new SharedSettings();
|
||||
}
|
||||
|
||||
private State myState = new State();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public State getState() {
|
||||
return myState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadState(State state) {
|
||||
myState = state;
|
||||
}
|
||||
|
||||
public static DiffSettingsHolder getInstance() {
|
||||
return ServiceManager.getService(DiffSettingsHolder.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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 com.intellij.diff.impl
|
||||
|
||||
import com.intellij.diff.util.DiffPlaces
|
||||
import com.intellij.diff.util.DiffUtil
|
||||
import com.intellij.openapi.components.PersistentStateComponent
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.components.State
|
||||
import com.intellij.openapi.components.Storage
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.util.xmlb.annotations.MapAnnotation
|
||||
import java.util.*
|
||||
|
||||
@State(
|
||||
name = "DiffSettings",
|
||||
storages = arrayOf(Storage(value = DiffUtil.DIFF_CONFIG))
|
||||
)
|
||||
class DiffSettingsHolder : PersistentStateComponent<DiffSettingsHolder.State> {
|
||||
companion object {
|
||||
@JvmField val KEY: Key<DiffSettings> = Key.create("DiffSettings")
|
||||
|
||||
@JvmStatic
|
||||
fun getInstance(): DiffSettingsHolder {
|
||||
return ServiceManager.getService(DiffSettingsHolder::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
internal data class SharedSettings(
|
||||
var GO_TO_NEXT_FILE_ON_NEXT_DIFFERENCE: Boolean = true
|
||||
)
|
||||
|
||||
internal data class PlaceSettings(
|
||||
var DIFF_TOOLS_ORDER: List<String> = ArrayList(),
|
||||
var SYNC_BINARY_EDITOR_SETTINGS: Boolean = true
|
||||
)
|
||||
|
||||
class DiffSettings internal constructor(val SHARED_SETTINGS: SharedSettings,
|
||||
val PLACE_SETTINGS: PlaceSettings) {
|
||||
constructor() : this(SharedSettings(), PlaceSettings())
|
||||
|
||||
var diffToolsOrder: List<String>
|
||||
get() = PLACE_SETTINGS.DIFF_TOOLS_ORDER
|
||||
set(order) { PLACE_SETTINGS.DIFF_TOOLS_ORDER = order }
|
||||
|
||||
var isGoToNextFileOnNextDifference: Boolean
|
||||
get() = SHARED_SETTINGS.GO_TO_NEXT_FILE_ON_NEXT_DIFFERENCE
|
||||
set(value) { SHARED_SETTINGS.GO_TO_NEXT_FILE_ON_NEXT_DIFFERENCE = value }
|
||||
|
||||
var isSyncBinaryEditorSettings: Boolean
|
||||
get() = PLACE_SETTINGS.SYNC_BINARY_EDITOR_SETTINGS
|
||||
set(value) { PLACE_SETTINGS.SYNC_BINARY_EDITOR_SETTINGS = value }
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getSettings(): DiffSettings {
|
||||
return getSettings(null)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getSettings(place: String?): DiffSettings {
|
||||
return getInstance().getSettings(place)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getSettings(place: String?): DiffSettings {
|
||||
val placeKey = place ?: DiffPlaces.DEFAULT
|
||||
val placeSettings = myState.PLACES_MAP.getOrPut(placeKey, { defaultPlaceSettings(placeKey) })
|
||||
return DiffSettings(myState.SHARED_SETTINGS, placeSettings)
|
||||
}
|
||||
|
||||
private fun copyStateWithoutDefaults(): State {
|
||||
val result = State()
|
||||
result.SHARED_SETTINGS = myState.SHARED_SETTINGS
|
||||
|
||||
myState.PLACES_MAP.entries.forEach {
|
||||
if (it.value != defaultPlaceSettings(it.key)) result.PLACES_MAP.put(it.key, it.value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun defaultPlaceSettings(place: String): PlaceSettings {
|
||||
return PlaceSettings()
|
||||
}
|
||||
|
||||
|
||||
class State {
|
||||
@MapAnnotation(surroundWithTag = false, surroundKeyWithTag = false, surroundValueWithTag = false)
|
||||
internal var PLACES_MAP: TreeMap<String, PlaceSettings> = TreeMap()
|
||||
internal var SHARED_SETTINGS = SharedSettings()
|
||||
}
|
||||
|
||||
private var myState: State = State()
|
||||
|
||||
override fun getState(): State {
|
||||
return copyStateWithoutDefaults()
|
||||
}
|
||||
|
||||
override fun loadState(state: State) {
|
||||
myState = state
|
||||
}
|
||||
}
|
||||
+26
-20
@@ -154,37 +154,43 @@ class TextDiffSettingsHolder : PersistentStateComponent<TextDiffSettingsHolder.S
|
||||
}
|
||||
|
||||
fun getSettings(place: String?): TextDiffSettings {
|
||||
val placeSettings = myState.PLACES_MAP.getOrPut(place ?: DiffPlaces.DEFAULT, { PlaceSettings() })
|
||||
val placeKey = place ?: DiffPlaces.DEFAULT
|
||||
val placeSettings = myState.PLACES_MAP.getOrPut(placeKey, { defaultPlaceSettings(placeKey) })
|
||||
return TextDiffSettings(myState.SHARED_SETTINGS, placeSettings)
|
||||
}
|
||||
|
||||
private fun copyStateWithoutDefaults(): State {
|
||||
val result = State()
|
||||
result.SHARED_SETTINGS = myState.SHARED_SETTINGS
|
||||
|
||||
myState.PLACES_MAP.entries.forEach {
|
||||
if (it.value != defaultPlaceSettings(it.key)) result.PLACES_MAP.put(it.key, it.value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun defaultPlaceSettings(place: String): PlaceSettings {
|
||||
val settings = PlaceSettings();
|
||||
if (place == DiffPlaces.CHANGES_VIEW) {
|
||||
settings.EXPAND_BY_DEFAULT = false
|
||||
}
|
||||
if (place == DiffPlaces.COMMIT_DIALOG) {
|
||||
settings.EXPAND_BY_DEFAULT = false
|
||||
}
|
||||
return settings
|
||||
}
|
||||
|
||||
|
||||
class State {
|
||||
@MapAnnotation(surroundWithTag = false, surroundKeyWithTag = false, surroundValueWithTag = false)
|
||||
internal var PLACES_MAP: TreeMap<String, PlaceSettings> = defaultPlaceSettings()
|
||||
internal var PLACES_MAP: TreeMap<String, PlaceSettings> = TreeMap()
|
||||
internal var SHARED_SETTINGS = SharedSettings()
|
||||
|
||||
companion object {
|
||||
private fun defaultPlaceSettings(): TreeMap<String, PlaceSettings> {
|
||||
val map = TreeMap<String, PlaceSettings>()
|
||||
|
||||
val changes = PlaceSettings()
|
||||
changes.EXPAND_BY_DEFAULT = false
|
||||
val commit = PlaceSettings()
|
||||
commit.EXPAND_BY_DEFAULT = false
|
||||
|
||||
map.put(DiffPlaces.DEFAULT, PlaceSettings())
|
||||
map.put(DiffPlaces.CHANGES_VIEW, changes)
|
||||
map.put(DiffPlaces.COMMIT_DIALOG, commit)
|
||||
|
||||
return map
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var myState: State = State()
|
||||
|
||||
override fun getState(): State {
|
||||
return myState
|
||||
return copyStateWithoutDefaults()
|
||||
}
|
||||
|
||||
override fun loadState(state: State) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-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.
|
||||
@@ -19,7 +19,10 @@ import com.intellij.psi.PsiNameIdentifierOwner;
|
||||
import com.intellij.psi.PsiNamedElement;
|
||||
import com.intellij.psi.StubBasedPsiElement;
|
||||
import com.jetbrains.python.psi.stubs.PyNamedParameterStub;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a named parameter, as opposed to a tuple parameter.
|
||||
@@ -39,10 +42,30 @@ public interface PyNamedParameter extends PyParameter, PsiNamedElement, PsiNameI
|
||||
boolean isKeywordOnly();
|
||||
|
||||
/**
|
||||
* @param includeDefaultValue if true, include the default value after an " = ".
|
||||
* @return Canonical representation of parameter. Includes asterisks for *param and **param, and name.
|
||||
* @param includeDefaultValue if true, include the default value after an "=".
|
||||
* @return canonical representation of parameter.
|
||||
* Includes asterisks for *param and **param, and name.
|
||||
*/
|
||||
@NotNull
|
||||
String getRepr(boolean includeDefaultValue);
|
||||
default String getRepr(boolean includeDefaultValue) {
|
||||
return getRepr(includeDefaultValue, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param includeDefaultValue if true, include the default value after an "=".
|
||||
* @param context context to be used to resolve argument type
|
||||
* @return canonical representation of parameter.
|
||||
* Includes asterisks for *param and **param, and name.
|
||||
* Also includes argument type if {@code context} is not null and resolved type is not unknown.
|
||||
*/
|
||||
@NotNull
|
||||
String getRepr(boolean includeDefaultValue, @Nullable TypeEvalContext context);
|
||||
|
||||
/**
|
||||
* @param context context to be used to resolve argument type
|
||||
* @return argument type. Returns element type for *param and value type for **param.
|
||||
*/
|
||||
@Nullable
|
||||
PyType getArgumentType(@NotNull TypeEvalContext context);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-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.
|
||||
@@ -166,7 +166,7 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
// formatting of hints: hint index -> flags. this includes flags for parens.
|
||||
final Map<Integer, EnumSet<ParameterInfoUIContextEx.Flag>> hintFlags = new HashMap<>();
|
||||
|
||||
final List<String> hintsList = buildParameterListHint(parameterList, namedParameters, parameterToIndex, hintFlags);
|
||||
final List<String> hintsList = buildParameterListHint(parameterList, namedParameters, parameterToIndex, hintFlags, typeEvalContext);
|
||||
|
||||
final int currentParamOffset = context.getCurrentParameterIndex(); // in Python mode, we get an offset here, not an index!
|
||||
|
||||
@@ -324,15 +324,17 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
/**
|
||||
* builds the textual picture and the list of named parameters
|
||||
*
|
||||
* @param parameters parameters of a callable
|
||||
* @param namedParameters used to collect all named parameters of callable
|
||||
* @param parameters parameters of a callable
|
||||
* @param namedParameters used to collect all named parameters of callable
|
||||
* @param parameterToIndex used to collect info about parameter indexes
|
||||
* @param hintFlags mark parameter as deprecated/highlighted/strikeout
|
||||
* @param hintFlags mark parameter as deprecated/highlighted/strikeout
|
||||
* @param context context to be used to get parameter representation
|
||||
*/
|
||||
private static List<String> buildParameterListHint(@NotNull List<PyParameter> parameters,
|
||||
@NotNull final List<PyNamedParameter> namedParameters,
|
||||
@NotNull final Map<PyNamedParameter, Integer> parameterToIndex,
|
||||
@NotNull final Map<Integer, EnumSet<ParameterInfoUIContextEx.Flag>> hintFlags) {
|
||||
@NotNull final Map<Integer, EnumSet<ParameterInfoUIContextEx.Flag>> hintFlags,
|
||||
@NotNull TypeEvalContext context) {
|
||||
final List<String> hintsList = new ArrayList<>();
|
||||
ParamHelper.walkDownParamArray(
|
||||
parameters.toArray(new PyParameter[parameters.size()]),
|
||||
@@ -350,7 +352,7 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
public void visitNamedParameter(PyNamedParameter param, boolean first, boolean last) {
|
||||
namedParameters.add(param);
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append(param.getRepr(true));
|
||||
stringBuilder.append(param.getRepr(true, context));
|
||||
if (!last) stringBuilder.append(", ");
|
||||
int hintIndex = hintsList.size();
|
||||
parameterToIndex.put(param, hintIndex);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-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.
|
||||
@@ -104,7 +104,7 @@ public class PyDocumentationBuilder {
|
||||
}
|
||||
else if (elementDefinition != null && outerElement instanceof PyReferenceExpression) {
|
||||
myBody.addItem(combUp("\nInferred type: "));
|
||||
PythonDocumentationProvider.describeExpressionTypeWithLinks(myBody, (PyReferenceExpression)outerElement, context);
|
||||
PythonDocumentationProvider.describeTypeWithLinks(context.getType((PyReferenceExpression)outerElement), context, outerElement, myBody);
|
||||
}
|
||||
|
||||
if (elementDefinition != null) {
|
||||
@@ -189,7 +189,7 @@ public class PyDocumentationBuilder {
|
||||
}
|
||||
if (typeString != null) {
|
||||
myBody.addItem(combUp(typeString));
|
||||
PythonDocumentationProvider.describeTypeWithLinks(myBody, elementDefinition, type, context);
|
||||
PythonDocumentationProvider.describeTypeWithLinks(type, context, elementDefinition, myBody);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-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.
|
||||
@@ -43,17 +43,25 @@ public class PyTypeModelBuilder {
|
||||
}
|
||||
|
||||
abstract static class TypeModel {
|
||||
abstract void accept(TypeVisitor visitor);
|
||||
abstract void accept(@NotNull TypeVisitor visitor);
|
||||
|
||||
@NotNull
|
||||
public String asString() {
|
||||
TypeToStringVisitor visitor = new TypeToStringVisitor();
|
||||
this.accept(visitor);
|
||||
final TypeToStringVisitor visitor = new TypeToStringVisitor();
|
||||
accept(visitor);
|
||||
return visitor.getString();
|
||||
}
|
||||
|
||||
public void toBodyWithLinks(@NotNull ChainIterable<String> body, @NotNull PsiElement anchor) {
|
||||
TypeToBodyWithLinksVisitor visitor = new TypeToBodyWithLinksVisitor(body, anchor);
|
||||
this.accept(visitor);
|
||||
final TypeToBodyWithLinksVisitor visitor = new TypeToBodyWithLinksVisitor(body, anchor);
|
||||
accept(visitor);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String asDescription() {
|
||||
final TypeToDescriptionVisitor visitor = new TypeToDescriptionVisitor();
|
||||
accept(visitor);
|
||||
return visitor.getDescription();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +73,7 @@ public class PyTypeModelBuilder {
|
||||
}
|
||||
|
||||
@Override
|
||||
void accept(TypeVisitor visitor) {
|
||||
void accept(@NotNull TypeVisitor visitor) {
|
||||
visitor.oneOf(this);
|
||||
}
|
||||
}
|
||||
@@ -80,21 +88,31 @@ public class PyTypeModelBuilder {
|
||||
}
|
||||
|
||||
@Override
|
||||
void accept(TypeVisitor visitor) {
|
||||
void accept(@NotNull TypeVisitor visitor) {
|
||||
visitor.collectionOf(this);
|
||||
}
|
||||
}
|
||||
|
||||
static class NamedType extends TypeModel {
|
||||
|
||||
@NotNull
|
||||
private static final NamedType ANY = new NamedType(PyNames.UNKNOWN_TYPE);
|
||||
|
||||
@Nullable
|
||||
private String name;
|
||||
|
||||
private NamedType(String name) {
|
||||
private NamedType(@Nullable String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
void accept(TypeVisitor visitor) {
|
||||
visitor.name(this.name);
|
||||
void accept(@NotNull TypeVisitor visitor) {
|
||||
visitor.name(name);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static NamedType nameOrAny(@Nullable PyType type) {
|
||||
return type == null ? ANY : new NamedType(type.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +124,7 @@ public class PyTypeModelBuilder {
|
||||
}
|
||||
|
||||
@Override
|
||||
void accept(TypeVisitor visitor) {
|
||||
void accept(@NotNull TypeVisitor visitor) {
|
||||
visitor.unknown(this);
|
||||
}
|
||||
}
|
||||
@@ -119,7 +137,7 @@ public class PyTypeModelBuilder {
|
||||
}
|
||||
|
||||
@Override
|
||||
void accept(TypeVisitor visitor) {
|
||||
void accept(@NotNull TypeVisitor visitor) {
|
||||
visitor.optional(this);
|
||||
}
|
||||
}
|
||||
@@ -134,31 +152,22 @@ public class PyTypeModelBuilder {
|
||||
}
|
||||
|
||||
@Override
|
||||
void accept(TypeVisitor visitor) {
|
||||
void accept(@NotNull TypeVisitor visitor) {
|
||||
visitor.tuple(this);
|
||||
}
|
||||
}
|
||||
|
||||
private static TypeModel _(String name) {
|
||||
return new NamedType(name);
|
||||
}
|
||||
|
||||
static class FunctionType extends TypeModel {
|
||||
private TypeModel returnType;
|
||||
@Nullable private Collection<TypeModel> parameters;
|
||||
@NotNull private final TypeModel returnType;
|
||||
@Nullable private final Collection<TypeModel> parameters;
|
||||
|
||||
FunctionType(@Nullable TypeModel returnType, @Nullable Collection<TypeModel> parameters) {
|
||||
if (returnType != null) {
|
||||
this.returnType = returnType;
|
||||
}
|
||||
else {
|
||||
this.returnType = _(PyNames.UNKNOWN_TYPE);
|
||||
}
|
||||
private FunctionType(@Nullable TypeModel returnType, @Nullable Collection<TypeModel> parameters) {
|
||||
this.returnType = returnType != null ? returnType : NamedType.ANY;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
void accept(TypeVisitor visitor) {
|
||||
void accept(@NotNull TypeVisitor visitor) {
|
||||
visitor.function(this);
|
||||
}
|
||||
}
|
||||
@@ -167,14 +176,13 @@ public class PyTypeModelBuilder {
|
||||
@Nullable private final String name;
|
||||
@Nullable private final TypeModel type;
|
||||
|
||||
|
||||
private ParamType(@Nullable String name, @Nullable TypeModel type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
void accept(TypeVisitor visitor) {
|
||||
void accept(@NotNull TypeVisitor visitor) {
|
||||
visitor.param(this);
|
||||
}
|
||||
}
|
||||
@@ -193,7 +201,7 @@ public class PyTypeModelBuilder {
|
||||
return evaluated;
|
||||
}
|
||||
if (myVisited.containsKey(type)) { //already evaluating?
|
||||
return type != null ? _(type.getName()) : _(PyNames.UNKNOWN_TYPE);
|
||||
return NamedType.nameOrAny(type);
|
||||
}
|
||||
myVisited.put(type, null); //mark as evaluating
|
||||
|
||||
@@ -244,7 +252,7 @@ public class PyTypeModelBuilder {
|
||||
result = build((PyCallableType)type);
|
||||
}
|
||||
if (result == null) {
|
||||
result = type != null ? _(type.getName()) : _(PyNames.UNKNOWN_TYPE);
|
||||
result = NamedType.nameOrAny(type);
|
||||
}
|
||||
myVisited.put(type, result);
|
||||
return result;
|
||||
@@ -348,10 +356,9 @@ public class PyTypeModelBuilder {
|
||||
|
||||
@Override
|
||||
protected void addType(String name) {
|
||||
PyType type = PyTypeParser.getTypeByName(myAnchor, name);
|
||||
final PyType type = PyTypeParser.getTypeByName(myAnchor, name);
|
||||
if (type instanceof PyClassType) {
|
||||
myBody.addWith(new DocumentationBuilderKit.LinkWrapper(PythonDocumentationProvider.LINK_TYPE_TYPENAME + name),
|
||||
$(name));
|
||||
myBody.addWith(new DocumentationBuilderKit.LinkWrapper(PythonDocumentationProvider.LINK_TYPE_TYPENAME + name), $(name));
|
||||
}
|
||||
else {
|
||||
add(name);
|
||||
@@ -359,6 +366,27 @@ public class PyTypeModelBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
private static class TypeToDescriptionVisitor extends TypeNameVisitor {
|
||||
|
||||
@NotNull
|
||||
private final StringBuilder myResult = new StringBuilder();
|
||||
|
||||
@Override
|
||||
protected void add(String s) {
|
||||
myResult.append(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addType(String name) {
|
||||
add(name);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getDescription() {
|
||||
return myResult.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private abstract static class TypeNameVisitor implements TypeVisitor {
|
||||
private int myDepth = 0;
|
||||
private final static int MAX_DEPTH = 6;
|
||||
@@ -371,16 +399,16 @@ public class PyTypeModelBuilder {
|
||||
return;
|
||||
}
|
||||
add("Union[");
|
||||
processList(oneOf.oneOfTypes, ", ");
|
||||
processList(oneOf.oneOfTypes);
|
||||
add("]");
|
||||
myDepth--;
|
||||
}
|
||||
|
||||
private void processList(Collection<TypeModel> list, String separator) {
|
||||
private void processList(@NotNull Collection<TypeModel> list) {
|
||||
boolean first = true;
|
||||
for (TypeModel t : list) {
|
||||
if (!first) {
|
||||
add(separator);
|
||||
add(", ");
|
||||
}
|
||||
else {
|
||||
first = false;
|
||||
@@ -403,7 +431,7 @@ public class PyTypeModelBuilder {
|
||||
final String typingName = PyTypingTypeProvider.TYPING_COLLECTION_CLASSES.get(name);
|
||||
addType(typingName != null ? typingName : name);
|
||||
add("[");
|
||||
processList(collectionOf.elementTypes, ", ");
|
||||
processList(collectionOf.elementTypes);
|
||||
add("]");
|
||||
myDepth--;
|
||||
}
|
||||
@@ -425,7 +453,7 @@ public class PyTypeModelBuilder {
|
||||
add("(");
|
||||
final Collection<TypeModel> parameters = function.parameters;
|
||||
if (parameters != null) {
|
||||
processList(parameters, ", ");
|
||||
processList(parameters);
|
||||
}
|
||||
else {
|
||||
add("...");
|
||||
@@ -469,7 +497,7 @@ public class PyTypeModelBuilder {
|
||||
@Override
|
||||
public void tuple(TupleType type) {
|
||||
add("Tuple[");
|
||||
processList(type.members, ", ");
|
||||
processList(type.members);
|
||||
if (type.homogeneous) {
|
||||
add(", ...");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-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.
|
||||
@@ -20,7 +20,6 @@ import com.intellij.lang.documentation.AbstractDocumentationProvider;
|
||||
import com.intellij.lang.documentation.ExternalDocumentationProvider;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -32,10 +31,8 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.QualifiedName;
|
||||
import com.intellij.util.Function;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.PythonDialectsTokenSetProvider;
|
||||
import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
|
||||
import com.jetbrains.python.console.PydevConsoleRunner;
|
||||
import com.jetbrains.python.console.PydevDocumentationProvider;
|
||||
import com.jetbrains.python.documentation.docstrings.DocStringUtil;
|
||||
@@ -151,7 +148,7 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i
|
||||
cat.addItem(escaper.apply(paramStr));
|
||||
if (!PyNames.INIT.equals(name)) {
|
||||
cat.addItem(escaper.apply("\nInferred type: "));
|
||||
getTypeDescription(fun, cat);
|
||||
describeTypeWithLinks(fun, cat);
|
||||
cat.addItem(BR);
|
||||
}
|
||||
return cat;
|
||||
@@ -171,48 +168,67 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i
|
||||
}
|
||||
}
|
||||
if (originalElement instanceof PyTypedElement) {
|
||||
result.append("\n").append(describeType((PyTypedElement)originalElement));
|
||||
final String typeName = getTypeName(((PyTypedElement)originalElement));
|
||||
result
|
||||
.append("\n")
|
||||
.append(String.format("Inferred type: %s", typeName));
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String describeType(@NotNull PyTypedElement element) {
|
||||
@NotNull
|
||||
private static String getTypeName(@NotNull PyTypedElement element) {
|
||||
final TypeEvalContext context = TypeEvalContext.userInitiated(element.getProject(), element.getContainingFile());
|
||||
return String.format("Inferred type: %s", getTypeName(context.getType(element), context));
|
||||
}
|
||||
|
||||
private static void getTypeDescription(@NotNull PyFunction fun, @NotNull ChainIterable<String> body) {
|
||||
final TypeEvalContext context = TypeEvalContext.userInitiated(fun.getProject(), fun.getContainingFile());
|
||||
final PyTypeModelBuilder builder = new PyTypeModelBuilder(context);
|
||||
builder.build(context.getType(fun), true).toBodyWithLinks(body, fun);
|
||||
return getTypeName(context.getType(element), context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type type which name will be calculated
|
||||
* @param context type evaluation context
|
||||
* @return string representation of the type
|
||||
*/
|
||||
@NotNull
|
||||
public static String getTypeName(@Nullable PyType type, @NotNull TypeEvalContext context) {
|
||||
final PyTypeModelBuilder.TypeModel typeModel = buildTypeModel(type, context);
|
||||
return typeModel.asString();
|
||||
return buildTypeModel(type, context).asString();
|
||||
}
|
||||
|
||||
private static PyTypeModelBuilder.TypeModel buildTypeModel(PyType type, TypeEvalContext context) {
|
||||
PyTypeModelBuilder builder = new PyTypeModelBuilder(context);
|
||||
return builder.build(type, true);
|
||||
private static void describeTypeWithLinks(@NotNull PyTypedElement element, @NotNull ChainIterable<String> body) {
|
||||
final TypeEvalContext context = TypeEvalContext.userInitiated(element.getProject(), element.getContainingFile());
|
||||
describeTypeWithLinks(context.getType(element), context, element, body);
|
||||
}
|
||||
|
||||
public static void describeExpressionTypeWithLinks(@NotNull ChainIterable<String> body,
|
||||
@NotNull PyReferenceExpression expression,
|
||||
@NotNull TypeEvalContext context) {
|
||||
final PyType type = context.getType(expression);
|
||||
describeTypeWithLinks(body, expression, type, context);
|
||||
}
|
||||
|
||||
public static void describeTypeWithLinks(@NotNull ChainIterable<String> body,
|
||||
/**
|
||||
* @param type type which description will be calculated.
|
||||
* Description is the same as {@link PythonDocumentationProvider#getTypeDescription(PyType, TypeEvalContext)} gives but
|
||||
* types are converted to links.
|
||||
* @param context type evaluation context
|
||||
* @param anchor anchor element
|
||||
* @param body body to be used to append description
|
||||
*/
|
||||
public static void describeTypeWithLinks(@Nullable PyType type,
|
||||
@NotNull TypeEvalContext context,
|
||||
@NotNull PsiElement anchor,
|
||||
PyType type, TypeEvalContext context) {
|
||||
final PyTypeModelBuilder builder = new PyTypeModelBuilder(context);
|
||||
builder.build(type, true).toBodyWithLinks(body, anchor);
|
||||
@NotNull ChainIterable<String> body) {
|
||||
buildTypeModel(type, context).toBodyWithLinks(body, anchor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param type type which description will be calculated
|
||||
* @param context type evaluation context
|
||||
* @return more user-friendly description than result of {@link PythonDocumentationProvider#getTypeName(PyType, TypeEvalContext)}.
|
||||
* <code>Any</code> is excluded from <code>Union[Any, ...]</code>-like types.
|
||||
*/
|
||||
@NotNull
|
||||
public static String getTypeDescription(@Nullable PyType type, @NotNull TypeEvalContext context) {
|
||||
return buildTypeModel(type, context).asDescription();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PyTypeModelBuilder.TypeModel buildTypeModel(@Nullable PyType type, @NotNull TypeEvalContext context) {
|
||||
return new PyTypeModelBuilder(context).build(type, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static ChainIterable<String> describeDecorators(@NotNull PyDecoratable what,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-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.
|
||||
@@ -189,10 +189,8 @@ public class PyTypeCheckerInspection extends PyInspection {
|
||||
problemsSet.add(checkMapping(results.getReceiver(), results.getArguments()));
|
||||
}
|
||||
if (!problemsSet.isEmpty()) {
|
||||
Map<PyExpression, Pair<String, ProblemHighlightType>> minProblems = Collections.min(
|
||||
problemsSet,
|
||||
(o1, o2) -> o1.size() - o2.size()
|
||||
);
|
||||
final Map<PyExpression, Pair<String, ProblemHighlightType>> minProblems = Collections.min(problemsSet,
|
||||
Comparator.comparingInt(Map::size));
|
||||
for (Map.Entry<PyExpression, Pair<String, ProblemHighlightType>> entry : minProblems.entrySet()) {
|
||||
registerProblem(entry.getKey(), entry.getValue().getFirst(), entry.getValue().getSecond());
|
||||
}
|
||||
@@ -222,7 +220,7 @@ public class PyTypeCheckerInspection extends PyInspection {
|
||||
for (Map.Entry<PyExpression, PyNamedParameter> entry : mapping.entrySet()) {
|
||||
final PyNamedParameter param = entry.getValue();
|
||||
final PyExpression arg = entry.getKey();
|
||||
final PyType expectedArgType = PyTypeChecker.getExpectedArgumentType(param, myTypeEvalContext);
|
||||
final PyType expectedArgType = param.getArgumentType(myTypeEvalContext);
|
||||
if (expectedArgType == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-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.
|
||||
@@ -28,6 +28,7 @@ import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.PlatformIcons;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
@@ -35,6 +36,7 @@ import com.jetbrains.python.PythonDialectsTokenSetProvider;
|
||||
import com.jetbrains.python.codeInsight.PyTypingTypeProvider;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
|
||||
import com.jetbrains.python.documentation.PythonDocumentationProvider;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.psi.stubs.PyNamedParameterStub;
|
||||
@@ -166,13 +168,25 @@ public class PyNamedParameterImpl extends PyBaseElementImpl<PyNamedParameterStub
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getRepr(boolean includeDefaultValue) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@Override
|
||||
public String getRepr(boolean includeDefaultValue, @Nullable TypeEvalContext context) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (isPositionalContainer()) sb.append("*");
|
||||
else if (isKeywordContainer()) sb.append("**");
|
||||
|
||||
sb.append(getName());
|
||||
|
||||
if (context != null) {
|
||||
final PyType argumentType = getArgumentType(context);
|
||||
if (argumentType != null) {
|
||||
sb.append(": ");
|
||||
sb.append(PythonDocumentationProvider.getTypeDescription(argumentType, context));
|
||||
}
|
||||
}
|
||||
|
||||
final PyExpression defaultValue = getDefaultValue();
|
||||
if (includeDefaultValue && defaultValue != null) {
|
||||
if (defaultValueShouldBeIncluded(includeDefaultValue, defaultValue)) {
|
||||
String representation = PyUtil.getReadableRepr(defaultValue, true);
|
||||
if (defaultValue instanceof PyStringLiteralExpression) {
|
||||
final Pair<String, String> quotes = PyStringLiteralUtil.getQuotes(defaultValue.getText());
|
||||
@@ -182,9 +196,36 @@ public class PyNamedParameterImpl extends PyBaseElementImpl<PyNamedParameterStub
|
||||
}
|
||||
sb.append("=").append(representation);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static boolean defaultValueShouldBeIncluded(boolean includeDefaultValue, @Nullable PyExpression defaultValue) {
|
||||
if (!includeDefaultValue || defaultValue == null) return false;
|
||||
|
||||
// In case of `None` default value, it will be listed in the type as `Optional[...]` or `Union[..., None, ...]`
|
||||
return !PyNames.NONE.equals(defaultValue.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PyType getArgumentType(@NotNull TypeEvalContext context) {
|
||||
final PyType parameterType = context.getType(this);
|
||||
|
||||
if (parameterType instanceof PyCollectionType) {
|
||||
final PyCollectionType paramCollectionType = (PyCollectionType)parameterType;
|
||||
|
||||
if (isPositionalContainer()) {
|
||||
return paramCollectionType.getIteratedItemType();
|
||||
}
|
||||
else if (isKeywordContainer()) {
|
||||
return ContainerUtil.getOrElse(paramCollectionType.getElementTypes(context), 1, null);
|
||||
}
|
||||
}
|
||||
|
||||
return parameterType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PyAnnotation getAnnotation() {
|
||||
return getStubOrPsiChild(PyElementTypes.ANNOTATION);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-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.
|
||||
@@ -487,18 +487,18 @@ public class PyTypeChecker {
|
||||
if (keywordParameter == null) keywordParameter = parameter;
|
||||
keywordTypes.add(actualArgType);
|
||||
}
|
||||
else if (!match(getExpectedArgumentType(parameter, context), actualArgType, context, substitutions)) {
|
||||
else if (!match(parameter.getArgumentType(context), actualArgType, context, substitutions)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (positionalParameter != null &&
|
||||
!match(getExpectedArgumentType(positionalParameter, context), PyUnionType.union(positionalTypes), context, substitutions)) {
|
||||
!match(positionalParameter.getArgumentType(context), PyUnionType.union(positionalTypes), context, substitutions)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (keywordParameter != null &&
|
||||
!match(getExpectedArgumentType(keywordParameter, context), PyUnionType.union(keywordTypes), context, substitutions)) {
|
||||
!match(keywordParameter.getArgumentType(context), PyUnionType.union(keywordTypes), context, substitutions)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -757,24 +757,6 @@ public class PyTypeChecker {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PyType getExpectedArgumentType(@NotNull PyNamedParameter parameter, @NotNull TypeEvalContext context) {
|
||||
final PyType parameterType = context.getType(parameter);
|
||||
|
||||
if (parameterType instanceof PyCollectionType) {
|
||||
final PyCollectionType paramCollectionType = (PyCollectionType)parameterType;
|
||||
|
||||
if (parameter.isPositionalContainer()) {
|
||||
return paramCollectionType.getIteratedItemType();
|
||||
}
|
||||
else if (parameter.isKeywordContainer()) {
|
||||
return ContainerUtil.getOrElse(paramCollectionType.getElementTypes(context), 1, null);
|
||||
}
|
||||
}
|
||||
|
||||
return parameterType;
|
||||
}
|
||||
|
||||
public static class AnalyzeCallResults {
|
||||
@NotNull private final PyCallable myCallable;
|
||||
@Nullable private final PyExpression myReceiver;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
from typing import Union, Any
|
||||
|
||||
def foo(a1: str, a2: str=None, a3: Union[str, int]=None, a4: Union[Any, int], *args: int, **kwargs: int):
|
||||
pass
|
||||
|
||||
|
||||
foo(<arg1>)
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-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.
|
||||
@@ -29,9 +29,9 @@ import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.jetbrains.python.fixtures.LightMarkedTestCase;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import com.jetbrains.python.psi.PyArgumentList;
|
||||
import com.jetbrains.python.psi.PyCallExpression;
|
||||
import junit.framework.Assert;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -53,7 +53,7 @@ public class PyParameterInfoTest extends LightMarkedTestCase {
|
||||
|
||||
protected Map<String, PsiElement> loadTest(int expected_marks) {
|
||||
Map<String, PsiElement> marks = loadTest();
|
||||
Assert.assertEquals("Test data sanity", marks.size(), expected_marks);
|
||||
assertEquals("Test data sanity", marks.size(), expected_marks);
|
||||
return marks;
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ public class PyParameterInfoTest extends LightMarkedTestCase {
|
||||
|
||||
public void testKwdArgInClass() {
|
||||
Map<String, PsiElement> marks = loadTest(1);
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self, **kw", new String[]{"**kw"}, new String[]{"self, "});
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self: A, **kw", new String[]{"**kw"}, new String[]{"self: A, "});
|
||||
}
|
||||
|
||||
public void testKwdArgOutOfOrder() {
|
||||
@@ -223,14 +223,14 @@ public class PyParameterInfoTest extends LightMarkedTestCase {
|
||||
public void testSimpleMethod() {
|
||||
Map<String, PsiElement> marks = loadTest(1);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self, a", new String[]{"a"}, new String[]{"self, "});
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self: A, a", new String[]{"a"}, new String[]{"self: A, "});
|
||||
}
|
||||
|
||||
public void testSimpleClassFunction() {
|
||||
Map<String, PsiElement> marks = loadTest(2);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self, a", new String[]{"self, "});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("self, a", new String[]{"a"});
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self: A, a", new String[]{"self: A, "});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("self: A, a", new String[]{"a"});
|
||||
}
|
||||
|
||||
public void testReassignedFunction() {
|
||||
@@ -243,65 +243,65 @@ public class PyParameterInfoTest extends LightMarkedTestCase {
|
||||
public void testReassignedInstanceMethod() {
|
||||
Map<String, PsiElement> marks = loadTest(3);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self, a, b, c", new String[]{"a, "}, new String[]{"self, "});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("self, a, b, c", new String[]{"b, "}, new String[]{"self, "});
|
||||
feignCtrlP(marks.get("<arg3>").getTextOffset()).check("self, a, b, c", new String[]{"c"}, new String[]{"self, "});
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self: Foo, a, b, c", new String[]{"a, "}, new String[]{"self: Foo, "});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("self: Foo, a, b, c", new String[]{"b, "}, new String[]{"self: Foo, "});
|
||||
feignCtrlP(marks.get("<arg3>").getTextOffset()).check("self: Foo, a, b, c", new String[]{"c"}, new String[]{"self: Foo, "});
|
||||
}
|
||||
|
||||
public void testReassignedClassInit() {
|
||||
Map<String, PsiElement> marks = loadTest(2);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self, a, b", new String[]{"a, "}, new String[]{"self, "});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("self, a, b", new String[]{"b"}, new String[]{"self, "});
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self: Bar, a, b", new String[]{"a, "}, new String[]{"self: Bar, "});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("self: Bar, a, b", new String[]{"b"}, new String[]{"self: Bar, "});
|
||||
}
|
||||
|
||||
public void testInheritedClassInit() {
|
||||
Map<String, PsiElement> marks = loadTest(2);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self, a, b", new String[]{"a, "}, new String[]{"self, "});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("self, a, b", new String[]{"b"}, new String[]{"self, "});
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self: Bar, a, b", new String[]{"a, "}, new String[]{"self: Bar, "});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("self: Bar, a, b", new String[]{"b"}, new String[]{"self: Bar, "});
|
||||
}
|
||||
|
||||
public void testRedefinedNewConstructorCall() {
|
||||
Map<String, PsiElement> marks = loadTest(2);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("cls, a, b", new String[]{"a, "}, new String[]{"cls, "});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("cls, a, b", new String[]{"b"}, new String[]{"cls, "});
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("cls: A, a, b", new String[]{"a, "}, new String[]{"cls: A, "});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("cls: A, a, b", new String[]{"b"}, new String[]{"cls: A, "});
|
||||
}
|
||||
|
||||
public void testRedefinedNewDirectCall() {
|
||||
Map<String, PsiElement> marks = loadTest(3);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("cls, a, b", new String[]{"cls, "});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("cls, a, b", new String[]{"a, "});
|
||||
feignCtrlP(marks.get("<arg3>").getTextOffset()).check("cls, a, b", new String[]{"b"});
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("cls: A, a, b", new String[]{"cls: A, "});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("cls: A, a, b", new String[]{"a, "});
|
||||
feignCtrlP(marks.get("<arg3>").getTextOffset()).check("cls: A, a, b", new String[]{"b"});
|
||||
}
|
||||
|
||||
public void testIgnoreNewInOldStyleClass() {
|
||||
Map<String, PsiElement> marks = loadTest(1);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self, one", new String[]{"one"}, new String[]{"self, "});
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self: A, one", new String[]{"one"}, new String[]{"self: A, "});
|
||||
}
|
||||
|
||||
|
||||
public void testBoundMethodSimple() {
|
||||
Map<String, PsiElement> marks = loadTest(2);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self, a, b", new String[]{"a, "}, new String[]{"self, "});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("self, a, b", new String[]{"b"}, new String[]{"self, "});
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self: A, a, b", new String[]{"a, "}, new String[]{"self: A, "});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("self: A, a, b", new String[]{"b"}, new String[]{"self: A, "});
|
||||
}
|
||||
|
||||
public void testBoundMethodReassigned() {
|
||||
Map<String, PsiElement> marks = loadTest(2);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self, a, b", new String[]{"a, "}, new String[]{"self, "});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("self, a, b", new String[]{"b"}, new String[]{"self, "});
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self: A, a, b", new String[]{"a, "}, new String[]{"self: A, "});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("self: A, a, b", new String[]{"b"}, new String[]{"self: A, "});
|
||||
}
|
||||
|
||||
public void testConstructorFactory() {
|
||||
Map<String, PsiElement> marks = loadTest(1);
|
||||
|
||||
feignCtrlP(marks.get("<arg>").getTextOffset()).check("self, color", new String[]{"color"}, new String[]{"self, "});
|
||||
feignCtrlP(marks.get("<arg>").getTextOffset()).check("self: Foo, color", new String[]{"color"}, new String[]{"self: Foo, "});
|
||||
}
|
||||
|
||||
|
||||
@@ -386,16 +386,16 @@ public class PyParameterInfoTest extends LightMarkedTestCase {
|
||||
public void testPartialNamed() {
|
||||
Map<String, PsiElement> marks = loadTest(2);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("a, b, c=1, d=2, e=3", new String[]{"d=2, "});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("a, b, c=1, d=2, e=3", new String[]{"e=3"}); // no logical next
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("a, b, c: int=1, d: int=2, e: int=3", new String[]{"d: int=2, "});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("a, b, c: int=1, d: int=2, e: int=3", new String[]{"e: int=3"}); // no logical next
|
||||
}
|
||||
|
||||
public void testPy3kPastTupleArg() {
|
||||
Map<String, PsiElement> marks = loadTest(4);
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("*arg, a=1, b=2", new String[]{"*arg, "});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("*arg, a=1, b=2", new String[]{"*arg, "});
|
||||
feignCtrlP(marks.get("<arg3>").getTextOffset()).check("*arg, a=1, b=2", new String[]{"b=2"});
|
||||
feignCtrlP(marks.get("<arg4>").getTextOffset()).check("*arg, a=1, b=2", new String[]{"a=1, "});
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("*arg, a: int=1, b: int=2", new String[]{"*arg, "});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("*arg, a: int=1, b: int=2", new String[]{"*arg, "});
|
||||
feignCtrlP(marks.get("<arg3>").getTextOffset()).check("*arg, a: int=1, b: int=2", new String[]{"b: int=2"});
|
||||
feignCtrlP(marks.get("<arg4>").getTextOffset()).check("*arg, a: int=1, b: int=2", new String[]{"a: int=1, "});
|
||||
}
|
||||
|
||||
public void testNoArgs() {
|
||||
@@ -405,14 +405,29 @@ public class PyParameterInfoTest extends LightMarkedTestCase {
|
||||
|
||||
public void testNoArgsException() {
|
||||
Map<String, PsiElement> marks = loadTest(1);
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("<no parameters>", new String[0], new String[]{"<no parameters>"});
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("<no parameters>", ArrayUtil.EMPTY_STRING_ARRAY, new String[]{"<no parameters>"});
|
||||
}
|
||||
|
||||
public void testMultilineStringDefault() {
|
||||
Map<String, PsiElement> marks = loadTest(1);
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("length=12, allowed_chars='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'",
|
||||
new String[]{"allowed_chars='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'"},
|
||||
new String[0]);
|
||||
final int offset = loadTest(1).get("<arg2>").getTextOffset();
|
||||
feignCtrlP(offset).check("length: int=12, allowed_chars: str='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'",
|
||||
new String[]{"allowed_chars: str='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'"},
|
||||
ArrayUtil.EMPTY_STRING_ARRAY);
|
||||
}
|
||||
|
||||
// PY-22005
|
||||
public void testWithSpecifiedType() {
|
||||
myFixture.copyDirectoryToProject("typing", "");
|
||||
|
||||
runWithLanguageLevel(
|
||||
LanguageLevel.PYTHON35,
|
||||
() -> {
|
||||
final int offset = loadTest(1).get("<arg1>").getTextOffset();
|
||||
final String expectedInfo = "a1: str, a2: Optional[str], a3: Union[str, int, None], a4: int, *args: int, **kwargs: int";
|
||||
|
||||
feignCtrlP(offset).check(expectedInfo, new String[]{"a1: str, "});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -427,7 +442,7 @@ public class PyParameterInfoTest extends LightMarkedTestCase {
|
||||
final PyArgumentList parameterOwner = handler.findElementForParameterInfo(collector);
|
||||
collector.setParameterOwner(parameterOwner); // finds arglist, sets items to show
|
||||
if (collector.getParameterOwner() != null) {
|
||||
Assert.assertEquals("Collected one analysis result", 1, collector.myItems.length);
|
||||
assertEquals("Collected one analysis result", 1, collector.myItems.length);
|
||||
handler.updateParameterInfo((PyArgumentList)collector.getParameterOwner(), collector); // moves offset to correct parameter
|
||||
handler.updateUI((PyCallExpression.PyArgumentsMapping)collector.getItemsToShow()[0], collector); // sets hint text and flags
|
||||
}
|
||||
@@ -501,7 +516,7 @@ public class PyParameterInfoTest extends LightMarkedTestCase {
|
||||
|
||||
@Override
|
||||
public void setParameterOwner(PsiElement o) {
|
||||
Assert.assertTrue("Found element is a python arglist", o == null || o instanceof PyArgumentList);
|
||||
assertTrue("Found element is a python arglist", o == null || o instanceof PyArgumentList);
|
||||
myParamOwner = (PyArgumentList)o;
|
||||
}
|
||||
|
||||
@@ -586,7 +601,7 @@ public class PyParameterInfoTest extends LightMarkedTestCase {
|
||||
* @param disabled expected disabled substrings of hint
|
||||
*/
|
||||
public void check(String text, String[] highlighted, String[] disabled) {
|
||||
Assert.assertEquals("Signature", text, StringUtil.join(myTexts, ""));
|
||||
assertEquals("Signature", text, StringUtil.join(myTexts, ""));
|
||||
StringBuilder wrongs = new StringBuilder();
|
||||
// see if highlighted matches
|
||||
Set<String> highlightSet = new HashSet<>();
|
||||
@@ -615,7 +630,7 @@ public class PyParameterInfoTest extends LightMarkedTestCase {
|
||||
}
|
||||
}
|
||||
//
|
||||
if (wrongs.length() > 0) Assert.fail(wrongs.toString());
|
||||
if (wrongs.length() > 0) fail(wrongs.toString());
|
||||
}
|
||||
|
||||
public void check(String text, String[] highlighted) {
|
||||
@@ -623,7 +638,7 @@ public class PyParameterInfoTest extends LightMarkedTestCase {
|
||||
}
|
||||
|
||||
public void assertNotFound() {
|
||||
Assert.assertNull(myParamOwner);
|
||||
assertNull(myParamOwner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user