001/* 002 * Copyright 2010-2013 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.jetbrains.jet.lang.diagnostics; 018 019 020import com.google.common.collect.ImmutableList; 021import com.google.common.collect.Lists; 022import com.intellij.lang.ASTNode; 023import com.intellij.openapi.util.TextRange; 024import com.intellij.psi.PsiElement; 025import com.intellij.psi.PsiNameIdentifierOwner; 026import org.jetbrains.annotations.NotNull; 027import org.jetbrains.jet.lang.psi.*; 028import org.jetbrains.jet.lexer.JetKeywordToken; 029import org.jetbrains.jet.lexer.JetTokens; 030 031import java.util.Arrays; 032import java.util.Collections; 033import java.util.List; 034 035public class PositioningStrategies { 036 037 public static final PositioningStrategy<PsiElement> DEFAULT = new PositioningStrategy<PsiElement>(); 038 039 public static final PositioningStrategy<JetDeclaration> DECLARATION_RETURN_TYPE = new PositioningStrategy<JetDeclaration>() { 040 @NotNull 041 @Override 042 public List<TextRange> mark(@NotNull JetDeclaration declaration) { 043 return markElement(getElementToMark(declaration)); 044 } 045 046 @Override 047 public boolean isValid(@NotNull JetDeclaration declaration) { 048 return !hasSyntaxErrors(getElementToMark(declaration)); 049 } 050 051 private PsiElement getElementToMark(@NotNull JetDeclaration declaration) { 052 JetTypeReference returnTypeRef = null; 053 PsiElement nameIdentifierOrPlaceholder = null; 054 if (declaration instanceof JetNamedFunction) { 055 JetFunction function = (JetNamedFunction) declaration; 056 returnTypeRef = function.getReturnTypeRef(); 057 nameIdentifierOrPlaceholder = function.getNameIdentifier(); 058 } 059 else if (declaration instanceof JetProperty) { 060 JetProperty property = (JetProperty) declaration; 061 returnTypeRef = property.getTypeRef(); 062 nameIdentifierOrPlaceholder = property.getNameIdentifier(); 063 } 064 else if (declaration instanceof JetPropertyAccessor) { 065 JetPropertyAccessor accessor = (JetPropertyAccessor) declaration; 066 returnTypeRef = accessor.getReturnTypeReference(); 067 nameIdentifierOrPlaceholder = accessor.getNamePlaceholder(); 068 } 069 070 if (returnTypeRef != null) return returnTypeRef; 071 if (nameIdentifierOrPlaceholder != null) return nameIdentifierOrPlaceholder; 072 return declaration; 073 } 074 }; 075 076 public static final PositioningStrategy<PsiNameIdentifierOwner> NAME_IDENTIFIER = new PositioningStrategy<PsiNameIdentifierOwner>() { 077 @NotNull 078 @Override 079 public List<TextRange> mark(@NotNull PsiNameIdentifierOwner element) { 080 PsiElement nameIdentifier = element.getNameIdentifier(); 081 if (nameIdentifier != null) { 082 return markElement(nameIdentifier); 083 } 084 return markElement(element); 085 } 086 }; 087 088 public static final PositioningStrategy<PsiNameIdentifierOwner> NAMED_ELEMENT = new PositioningStrategy<PsiNameIdentifierOwner>() { 089 @NotNull 090 @Override 091 public List<TextRange> mark(@NotNull PsiNameIdentifierOwner element) { 092 if (element instanceof JetNamedFunction) { 093 JetNamedFunction function = (JetNamedFunction)element; 094 PsiElement endOfSignatureElement; 095 JetParameterList valueParameterList = function.getValueParameterList(); 096 JetElement returnTypeRef = function.getReturnTypeRef(); 097 PsiElement nameIdentifier = function.getNameIdentifier(); 098 if (returnTypeRef != null) { 099 endOfSignatureElement = returnTypeRef; 100 } 101 else if (valueParameterList != null) { 102 endOfSignatureElement = valueParameterList; 103 } 104 else if (nameIdentifier != null) { 105 endOfSignatureElement = nameIdentifier; 106 } 107 else { 108 endOfSignatureElement = function; 109 } 110 return markRange(new TextRange( 111 function.getTextRange().getStartOffset(), endOfSignatureElement.getTextRange().getEndOffset())); 112 } 113 else if (element instanceof JetProperty) { 114 JetProperty property = (JetProperty) element; 115 PsiElement endOfSignatureElement; 116 JetTypeReference propertyTypeRef = property.getTypeRef(); 117 PsiElement nameIdentifier = property.getNameIdentifier(); 118 if (propertyTypeRef != null) { 119 endOfSignatureElement = propertyTypeRef; 120 } 121 else if (nameIdentifier != null) { 122 endOfSignatureElement = nameIdentifier; 123 } 124 else { 125 endOfSignatureElement = property; 126 } 127 return markRange(new TextRange( 128 property.getTextRange().getStartOffset(), endOfSignatureElement.getTextRange().getEndOffset())); 129 } 130 else if (element instanceof JetClass) { 131 // primary constructor 132 JetClass klass = (JetClass)element; 133 PsiElement nameAsDeclaration = klass.getNameIdentifier(); 134 if (nameAsDeclaration == null) { 135 return markElement(klass); 136 } 137 PsiElement primaryConstructorParameterList = klass.getPrimaryConstructorParameterList(); 138 if (primaryConstructorParameterList == null) { 139 return markRange(nameAsDeclaration.getTextRange()); 140 } 141 return markRange(new TextRange( 142 nameAsDeclaration.getTextRange().getStartOffset(), primaryConstructorParameterList.getTextRange().getEndOffset())); 143 } 144 return super.mark(element); 145 } 146 @Override 147 public boolean isValid(@NotNull PsiNameIdentifierOwner element) { 148 return element.getNameIdentifier() != null && super.isValid(element); 149 } 150 }; 151 152 public static final PositioningStrategy<JetDeclaration> DECLARATION = new PositioningStrategy<JetDeclaration>() { 153 @NotNull 154 @Override 155 public List<TextRange> mark(@NotNull JetDeclaration element) { 156 if (element instanceof PsiNameIdentifierOwner) { 157 return NAMED_ELEMENT.mark((PsiNameIdentifierOwner) element); 158 } 159 return super.mark(element); 160 } 161 162 @Override 163 public boolean isValid(@NotNull JetDeclaration element) { 164 if (element instanceof PsiNameIdentifierOwner) { 165 return NAMED_ELEMENT.isValid((PsiNameIdentifierOwner) element); 166 } 167 return super.isValid(element); 168 } 169 }; 170 171 public static final PositioningStrategy<JetModifierListOwner> ABSTRACT_MODIFIER = modifierSetPosition(JetTokens.ABSTRACT_KEYWORD); 172 173 public static final PositioningStrategy<JetModifierListOwner> OVERRIDE_MODIFIER = modifierSetPosition(JetTokens.OVERRIDE_KEYWORD); 174 175 public static final PositioningStrategy<JetModifierListOwner> FINAL_MODIFIER = modifierSetPosition(JetTokens.FINAL_KEYWORD); 176 177 public static final PositioningStrategy<JetModifierListOwner> VARIANCE_MODIFIER = modifierSetPosition(JetTokens.IN_KEYWORD, 178 JetTokens.OUT_KEYWORD); 179 public static final PositioningStrategy<PsiElement> FOR_REDECLARATION = new PositioningStrategy<PsiElement>() { 180 @NotNull 181 @Override 182 public List<TextRange> mark(@NotNull PsiElement element) { 183 if (element instanceof JetNamedDeclaration) { 184 PsiElement nameIdentifier = ((JetNamedDeclaration) element).getNameIdentifier(); 185 if (nameIdentifier != null) { 186 return markElement(nameIdentifier); 187 } 188 } 189 else if (element instanceof JetFile) { 190 JetFile file = (JetFile) element; 191 PsiElement nameIdentifier = file.getNamespaceHeader().getNameIdentifier(); 192 if (nameIdentifier != null) { 193 return markElement(nameIdentifier); 194 } 195 } 196 return markElement(element); 197 } 198 }; 199 public static final PositioningStrategy<JetReferenceExpression> FOR_UNRESOLVED_REFERENCE = 200 new PositioningStrategy<JetReferenceExpression>() { 201 @NotNull 202 @Override 203 public List<TextRange> mark(@NotNull JetReferenceExpression element) { 204 if (element instanceof JetArrayAccessExpression) { 205 List<TextRange> ranges = ((JetArrayAccessExpression) element).getBracketRanges(); 206 if (!ranges.isEmpty()) { 207 return ranges; 208 } 209 } 210 return Collections.singletonList(element.getTextRange()); 211 } 212 }; 213 214 public static PositioningStrategy<JetModifierListOwner> modifierSetPosition(final JetKeywordToken... tokens) { 215 return new PositioningStrategy<JetModifierListOwner>() { 216 @NotNull 217 @Override 218 public List<TextRange> mark(@NotNull JetModifierListOwner modifierListOwner) { 219 JetModifierList modifierList = modifierListOwner.getModifierList(); 220 assert modifierList != null : "No modifier list, but modifier has been found by the analyzer"; 221 222 for (JetKeywordToken token : tokens) { 223 ASTNode node = modifierList.getModifierNode(token); 224 if (node != null) { 225 return markNode(node); 226 } 227 } 228 throw new IllegalStateException("None of the modifiers is found: " + Arrays.asList(tokens)); 229 } 230 }; 231 } 232 233 public static final PositioningStrategy<JetArrayAccessExpression> ARRAY_ACCESS = new PositioningStrategy<JetArrayAccessExpression>() { 234 @NotNull 235 @Override 236 public List<TextRange> mark(@NotNull JetArrayAccessExpression element) { 237 return markElement(element.getIndicesNode()); 238 } 239 }; 240 241 public static final PositioningStrategy<JetModifierListOwner> VISIBILITY_MODIFIER = new PositioningStrategy<JetModifierListOwner>() { 242 @NotNull 243 @Override 244 public List<TextRange> mark(@NotNull JetModifierListOwner element) { 245 List<JetKeywordToken> visibilityTokens = Lists.newArrayList( 246 JetTokens.PRIVATE_KEYWORD, JetTokens.PROTECTED_KEYWORD, JetTokens.PUBLIC_KEYWORD, JetTokens.INTERNAL_KEYWORD); 247 List<TextRange> result = Lists.newArrayList(); 248 for (JetKeywordToken token : visibilityTokens) { 249 if (element.hasModifier(token)) { 250 //noinspection ConstantConditions 251 result.add(element.getModifierList().getModifierNode(token).getTextRange()); 252 } 253 } 254 255 if (!result.isEmpty()) return result; 256 257 // Try to resolve situation when there's no visibility modifiers written before element 258 259 if (element instanceof PsiNameIdentifierOwner) { 260 PsiElement nameIdentifier = ((PsiNameIdentifierOwner) element).getNameIdentifier(); 261 if (nameIdentifier != null) { 262 return ImmutableList.of(nameIdentifier.getTextRange()); 263 } 264 } 265 266 if (element instanceof JetPropertyAccessor) { 267 return ImmutableList.of(((JetPropertyAccessor) element).getNamePlaceholder().getTextRange()); 268 } 269 270 if (element instanceof JetClassInitializer) { 271 return ImmutableList.of(element.getTextRange()); 272 } 273 274 if (element instanceof JetClassObject) { 275 JetObjectDeclaration objectDeclaration = ((JetClassObject) element).getObjectDeclaration(); 276 if (objectDeclaration != null) { 277 return ImmutableList.of(objectDeclaration.getObjectKeyword().getTextRange()); 278 } 279 } 280 281 throw new IllegalArgumentException( 282 String.format("Can't find text range for element '%s' with the text '%s'", 283 element.getClass().getCanonicalName(), element.getText())); 284 } 285 }; 286 287 public static final PositioningStrategy<JetTypeProjection> VARIANCE_IN_PROJECTION = new PositioningStrategy<JetTypeProjection>() { 288 @NotNull 289 @Override 290 public List<TextRange> mark(@NotNull JetTypeProjection element) { 291 return markNode(element.getProjectionNode()); 292 } 293 }; 294 295 public static final PositioningStrategy<JetParameter> PARAMETER_DEFAULT_VALUE = new PositioningStrategy<JetParameter>() { 296 @NotNull 297 @Override 298 public List<TextRange> mark(@NotNull JetParameter element) { 299 return markNode(element.getDefaultValue().getNode()); 300 } 301 }; 302 303 public static final PositioningStrategy<PsiElement> CALL_ELEMENT = new PositioningStrategy<PsiElement>() { 304 @NotNull 305 @Override 306 public List<TextRange> mark(@NotNull PsiElement callElement) { 307 if (callElement instanceof JetCallElement) { 308 JetExpression calleeExpression = ((JetCallElement) callElement).getCalleeExpression(); 309 if (calleeExpression != null) { 310 return markElement(calleeExpression); 311 } 312 } 313 return markElement(callElement); 314 } 315 }; 316 317 public static final PositioningStrategy<JetDeclarationWithBody> DECLARATION_WITH_BODY = new PositioningStrategy<JetDeclarationWithBody>() { 318 @NotNull 319 @Override 320 public List<TextRange> mark(@NotNull JetDeclarationWithBody element) { 321 JetExpression bodyExpression = element.getBodyExpression(); 322 if ((bodyExpression instanceof JetBlockExpression)) { 323 TextRange lastBracketRange = ((JetBlockExpression) bodyExpression).getLastBracketRange(); 324 if (lastBracketRange != null) { 325 return markRange(lastBracketRange); 326 } 327 } 328 return markElement(element); 329 } 330 331 @Override 332 public boolean isValid(@NotNull JetDeclarationWithBody element) { 333 if (!super.isValid(element)) return false; 334 335 JetExpression bodyExpression = element.getBodyExpression(); 336 if (!(bodyExpression instanceof JetBlockExpression)) return false; 337 if (((JetBlockExpression) bodyExpression).getLastBracketRange() == null) return false; 338 return true; 339 } 340 }; 341 342 public static final PositioningStrategy<JetProperty> VAL_OR_VAR_NODE = new PositioningStrategy<JetProperty>() { 343 @NotNull 344 @Override 345 public List<TextRange> mark(@NotNull JetProperty property) { 346 return markNode(property.getValOrVarNode()); 347 } 348 }; 349 350 public static final PositioningStrategy<JetWhenEntry> ELSE_ENTRY = new PositioningStrategy<JetWhenEntry>() { 351 @NotNull 352 @Override 353 public List<TextRange> mark(@NotNull JetWhenEntry entry) { 354 PsiElement elseKeywordElement = entry.getElseKeywordElement(); 355 assert elseKeywordElement != null; 356 return markElement(elseKeywordElement); 357 } 358 }; 359 360 public static final PositioningStrategy<JetWhenExpression> WHEN_EXPRESSION = new PositioningStrategy<JetWhenExpression>() { 361 @NotNull 362 @Override 363 public List<TextRange> mark(@NotNull JetWhenExpression element) { 364 return markElement(element.getWhenKeywordElement()); 365 } 366 }; 367 368 public static final PositioningStrategy<JetWhenConditionInRange> WHEN_CONDITION_IN_RANGE = 369 new PositioningStrategy<JetWhenConditionInRange>() { 370 @NotNull 371 @Override 372 public List<TextRange> mark(@NotNull JetWhenConditionInRange condition) { 373 return markElement(condition.getOperationReference()); 374 } 375 }; 376 377 public static final PositioningStrategy<JetNullableType> NULLABLE_TYPE = new PositioningStrategy<JetNullableType>() { 378 @NotNull 379 @Override 380 public List<TextRange> mark(@NotNull JetNullableType element) { 381 return markNode(element.getQuestionMarkNode()); 382 } 383 }; 384 385 public static final PositioningStrategy<JetExpression> CALL_EXPRESSION = new PositioningStrategy<JetExpression>() { 386 @NotNull 387 @Override 388 public List<TextRange> mark(@NotNull JetExpression element) { 389 if (element instanceof JetCallExpression) { 390 JetCallExpression callExpression = (JetCallExpression) element; 391 PsiElement endElement; 392 JetTypeArgumentList typeArgumentList = callExpression.getTypeArgumentList(); 393 JetExpression calleeExpression = callExpression.getCalleeExpression(); 394 if (typeArgumentList != null) { 395 endElement = typeArgumentList; 396 } 397 else if (calleeExpression != null) { 398 endElement = calleeExpression; 399 } 400 else { 401 endElement = element; 402 } 403 return markRange(new TextRange(element.getTextRange().getStartOffset(), endElement.getTextRange().getEndOffset())); 404 } 405 return super.mark(element); 406 } 407 }; 408 409 public static final PositioningStrategy<JetElement> VALUE_ARGUMENTS = new PositioningStrategy<JetElement>() { 410 @NotNull 411 @Override 412 public List<TextRange> mark(@NotNull JetElement element) { 413 if (element instanceof JetValueArgumentList) { 414 PsiElement rightParenthesis = ((JetValueArgumentList) element).getRightParenthesis(); 415 if (rightParenthesis != null) { 416 return markElement(rightParenthesis); 417 } 418 419 } 420 return super.mark(element); 421 } 422 }; 423 424 public static final PositioningStrategy<JetFunctionLiteral> FUNCTION_LITERAL_PARAMETERS = new PositioningStrategy<JetFunctionLiteral>() { 425 @NotNull 426 @Override 427 public List<TextRange> mark(@NotNull JetFunctionLiteral functionLiteral) { 428 JetParameterList valueParameterList = functionLiteral.getValueParameterList(); 429 if (valueParameterList != null) { 430 return markElement(valueParameterList); 431 } 432 return markNode(functionLiteral.getOpenBraceNode()); 433 } 434 }; 435 436 private PositioningStrategies() { 437 } 438}