/* * Copyright 2013-2016 Sergey Ignatov, Alexander Zolotov, Florin Patan * * 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.goide.completion; import com.goide.psi.*; import com.goide.psi.impl.GoPsiImplUtil; import com.intellij.psi.PsiElement; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.List; import java.util.Set; class GoStructLiteralCompletion { /** * Describes struct literal completion variants that should be suggested. */ enum Variants { /** * Only struct field names should be suggested. * Indicates that field:value initializers are used in this struct literal. * For example, {@code Struct{field1: "", caret}}. */ FIELD_NAME_ONLY, /** * Only values should be suggested. * Indicates that value initializers are used in this struct literal. * For example, {@code Struct{"", caret}}. */ VALUE_ONLY, /** * Both struct field names and values should be suggested. * Indicates that there's no reliable way to determine whether field:value or value initializers are used. * Example 1: {@code Struct{caret}}. * Example 2: {@code Struct{field1:"", "", caret}} */ BOTH, /** * Indicates that struct literal completion should not be available. */ NONE } @NotNull static Variants allowedVariants(@Nullable GoReferenceExpression structFieldReference) { GoValue value = parent(structFieldReference, GoValue.class); GoElement element = parent(value, GoElement.class); if (element != null && element.getKey() != null) { return Variants.NONE; } GoType type = GoPsiImplUtil.getLiteralType(element, true); if (!(type instanceof GoStructType)) { return Variants.NONE; } boolean hasValueInitializers = false; boolean hasFieldValueInitializers = false; GoLiteralValue literalValue = parent(element, GoLiteralValue.class); List<GoElement> fieldInitializers = literalValue != null ? literalValue.getElementList() : Collections.emptyList(); for (GoElement initializer : fieldInitializers) { if (initializer == element) { continue; } PsiElement colon = initializer.getColon(); hasFieldValueInitializers |= colon != null; hasValueInitializers |= colon == null; } return hasFieldValueInitializers && !hasValueInitializers ? Variants.FIELD_NAME_ONLY : !hasFieldValueInitializers && hasValueInitializers ? Variants.VALUE_ONLY : Variants.BOTH; } @NotNull static Set<String> alreadyAssignedFields(@Nullable GoLiteralValue literal) { if (literal == null) { return Collections.emptySet(); } return ContainerUtil.map2SetNotNull(literal.getElementList(), element -> { GoKey key = element.getKey(); GoFieldName fieldName = key != null ? key.getFieldName() : null; PsiElement identifier = fieldName != null ? fieldName.getIdentifier() : null; return identifier != null ? identifier.getText() : null; }); } @Contract("null,_->null") private static <T> T parent(@Nullable PsiElement of, @NotNull Class<T> parentClass) { return ObjectUtils.tryCast(of != null ? of.getParent() : null, parentClass); } }