public static GlobalSearchScope varDefSearchScope(BashVar reference, boolean withIncludedFiles) { PsiFile referenceFile = BashPsiUtils.findFileContext(reference); if (!withIncludedFiles) { return GlobalSearchScope.fileScope(referenceFile.getProject(), referenceFile.getVirtualFile()); } Set<VirtualFile> result = Sets.newLinkedHashSet(); result.add(referenceFile.getVirtualFile()); int referenceFileOffset = BashPsiUtils.getFileTextOffset(reference); BashFunctionDef referenceFunctionContainer = BashPsiUtils.findNextVarDefFunctionDefScope(reference); for (BashIncludeCommand command : BashPsiUtils.findIncludeCommands(referenceFile, null)) { boolean includeIsInFunction = BashPsiUtils.findNextVarDefFunctionDefScope(command) != null; //either one of var or include command is in a function or the var is used after the include command if (referenceFunctionContainer != null || includeIsInFunction || (referenceFileOffset > BashPsiUtils.getFileTextEndOffset(command))) { BashFileReference fileReference = command.getFileReference(); PsiFile includedFile = fileReference != null ? fileReference.findReferencedFile() : null; if (includedFile != null) { result.add(includedFile.getVirtualFile()); //also, add all files included in the valid include command's file for (PsiFile file : BashPsiUtils.findIncludedFiles(includedFile, true)) { result.add(file.getVirtualFile()); } } } } return GlobalSearchScope.filesScope(referenceFile.getProject(), result); }
public static List<PsiComment> findDocumentationElementComments(PsiElement element) { PsiElement command = findStubParent(element, BashCommand.class); if (command == null) { command = findStubParent(element, BashFunctionDef.class); } if (command == null) { return Collections.emptyList(); } int previousLine = getElementLineNumber(element); PsiElement current = command.getPrevSibling(); List<PsiComment> result = Lists.newLinkedList(); while (current != null && current.getNode() != null && current.getNode().getElementType() == BashTokenTypes.LINE_FEED) { current = current.getPrevSibling(); if (current instanceof PsiComment && BashPsiUtils.getElementEndLineNumber(current) + 1 == previousLine) { result.add(0, (PsiComment) current); previousLine = getElementLineNumber(current); current = current.getPrevSibling(); } else { break; } } return result; }
private static boolean containsUnsupportedVars(PsiElement fileReference) { AtomicBoolean otherVars = new AtomicBoolean(false); BashPsiUtils.visitRecursively(fileReference, new BashVisitor() { @Override public void visitVarUse(BashVar var) { if (!"HOME".equals(var.getReferenceName())) { otherVars.set(true); } } }); return otherVars.get(); }
public static boolean isValidReferenceScope(PsiElement referenceToDefCandidate, PsiElement variableDefinition) { PsiFile definitionFile = findFileContext(variableDefinition); PsiFile referenceFile = findFileContext(referenceToDefCandidate); boolean sameFile = definitionFile.equals(referenceFile); if (sameFile) { if (!isValidGlobalOffset(referenceToDefCandidate, variableDefinition)) { return false; } } else { //we need to find the include command and check the offset //the include command must fullfil the same condition as the normal variable definition above: //either var use and definition are both in functions or it the use is invalid List<BashIncludeCommand> includeCommands = findIncludeCommands(referenceFile, definitionFile); //currently we only support global include commands for (BashCommand includeCommand : includeCommands) { if (!isValidGlobalOffset(referenceToDefCandidate, includeCommand)) { return false; } } } return true; }
/** * A local var def is a valid definition for our start element if it's scope contains the start * element. * <br> * Also, the checked variable definition has to appear before the start element. * * @param varDef The variable definition in question * @param resolveState * @return True if varDef is a valid local definition for startElement */ protected boolean isValidLocalDefinition(BashVarDef varDef, ResolveState resolveState) { boolean validScope = PsiTreeUtil.isAncestor(BashPsiUtils.findEnclosingBlock(varDef), startElement, false); //fixme: this is not entirely true, think of a function with a var redefinition of a local variable of the inner functions //context (i.e. the outer function) //for now, this is ok return validScope && BashPsiUtils.getFileTextOffset(varDef) < BashPsiUtils.getFileTextOffset(startElement); }
public BashVarProcessor(BashVar startElement, String variableName, boolean checkLocalness, boolean leaveInjectionHosts, boolean preferNeighbourhood) { super(preferNeighbourhood); this.startElement = startElement; this.checkLocalness = checkLocalness; this.varName = variableName; this.startElementScope = BashPsiUtils.findNextVarDefFunctionDefScope(startElement); this.ignoreGlobals = false; this.leaveInjectionHost = leaveInjectionHosts; this.functionVarDefsAreGlobal = BashProjectSettings.storedSettings(startElement.getProject()).isGlobalFunctionVarDefs(); this.startElementTextOffset = BashPsiUtils.getFileTextOffset(startElement); }
public boolean isLocalVarDef() { //check if the command is a local-var defining command, e.g. local final PsiElement context = getContext(); if (context instanceof BashCommand) { final BashCommand parentCmd = (BashCommand) context; String commandName = parentCmd.getReferencedCommandName(); //declared by "local" if (parentCmd.isVarDefCommand() && LanguageBuiltins.localVarDefCommands.contains(commandName)) { return true; } //declared by either delcare or typeset in a function block if (localVarDefCommands.contains(commandName) && BashPsiUtils.findNextVarDefFunctionDefScope(context) != null) { return true; } } return false; }
PsiFile psiFile = BashPsiUtils.findFileContext(bashVar); VirtualFile virtualFile = psiFile.getVirtualFile(); Collection<BashIncludeCommand> includeCommands = StubIndex.getElements(BashIncludeCommandIndex.KEY, filePath, project, fileScope, BashIncludeCommand.class); if (!includeCommands.isEmpty()) { boolean varIsInFunction = BashPsiUtils.findNextVarDefFunctionDefScope(bashVar) != null; boolean includeIsInFunction = BashPsiUtils.findNextVarDefFunctionDefScope(command) != null; if (varIsInFunction || includeIsInFunction || (BashPsiUtils.getFileTextOffset(bashVar) > BashPsiUtils.getFileTextEndOffset(command))) { command.processDeclarations(processor, resolveState, command, bashVar);
BashFunctionDef varDefScope = BashPsiUtils.findNextVarDefFunctionDefScope(varDef); if (ignoreGlobals && varDefScope == null) { return false; boolean sameFiles = BashPsiUtils.findFileContext(startElement).equals(BashPsiUtils.findFileContext(varDef)); if (sameFiles) { int textOffsetVarDef = BashPsiUtils.getFileTextOffset(varDef); if (startElementTextOffset >= textOffsetVarDef) { return isDefinitionOffsetValid(varDefScope); VirtualFile varDefFile = BashPsiUtils.findFileContext(varDef).getVirtualFile(); Collection<PsiElement> includeCommands = includedFiles != null ? includedFiles.get(varDefFile) : null; if (includeCommands == null || includeCommands.isEmpty()) { BashFunctionDef includeCommandScope = BashPsiUtils.findNextVarDefFunctionDefScope(includeCommand); int startOffset = BashPsiUtils.getFileTextOffset(startElement); int endOffset = BashPsiUtils.getFileTextOffset(includeCommand); if (startOffset >= endOffset) { return isDefinitionOffsetValid(includeCommandScope); return BashPsiUtils.findNextVarDefFunctionDefScope(includeCommand) != null;
BashFunctionProcessor p = new BashFunctionProcessor(functionDef.getName(), true); boolean isOnGlobalLevel = BashPsiUtils.findNextVarDefFunctionDefScope(functionDef) == null; PsiElement start = functionDef.getContext() != null && !isOnGlobalLevel ? functionDef.getContext() String message = String.format("The function '%s' is already defined at line %d.", functionDef.getName(), BashPsiUtils.getElementLineNumber(firstFunctionDef));
@Nullable @Override public PsiElement resolveInner() { final String markerName = marker.getMarkerText(); if (markerName == null || markerName.isEmpty()) { return null; } //walk to the command containing this heredoc start marker //then walk the command's siblings to return the fist matching locator BashComposedCommand parent = BashPsiUtils.findParent(marker, BashComposedCommand.class); if (parent == null) { return null; } final List<BashHereDocEndMarker> endMarkers = Lists.newLinkedList(); BashPsiUtils.visitRecursively(parent, new BashVisitor() { @Override public void visitHereDocEndMarker(BashHereDocEndMarker marker) { endMarkers.add(marker); } }); //find out which position the marker is in a list of multiple, all start markers are wrapped in a single parent (a RedirectList) int markerPos = 0; for (PsiElement sibling = marker.getPrevSibling(); sibling != null; sibling = sibling.getPrevSibling()) { if (sibling instanceof BashHereDocMarker) { markerPos++; } } return endMarkers.size() > markerPos ? endMarkers.get(markerPos) : null; }
@Override public boolean isInplaceRenameAvailable(@NotNull PsiElement element, PsiElement context) { PsiFile fileContext = BashPsiUtils.findFileContext(element); if (context == null || fileContext == null || !fileContext.isEquivalentTo(BashPsiUtils.findFileContext(context))) { return false; } return (element instanceof BashVarDef) || (element instanceof BashFunctionDef) || (element instanceof BashVar) || (element instanceof BashHereDocMarker) || (element instanceof BashFileReference); } }
public static void collectIncludedFiles(PsiFile file, Set<PsiFile> files, boolean followNestedFiles) { String filePath = file.getVirtualFile().getPath(); Collection<BashIncludeCommand> commands = StubIndex.getElements(BashIncludeCommandIndex.KEY, filePath, file.getProject(), GlobalSearchScope.fileScope(file), BashIncludeCommand.class); for (BashIncludeCommand command : commands) { PsiFile includedFile = findIncludedFile(command); if (includedFile != null) { boolean followFile = followNestedFiles && !files.contains(includedFile); files.add(includedFile); if (followFile) { collectIncludedFiles(includedFile, files, true); } } } }
@Override public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) { PsiFile checkedFile = BashPsiUtils.findFileContext(file); if (checkedFile instanceof BashFile && !BashPsiUtils.isInjectedElement(file) && !isSpecialBashFile(checkedFile.getName())) { BashFile bashFile = (BashFile) checkedFile; Boolean isLanguageConsole = checkedFile.getUserData(BashFile.LANGUAGE_CONSOLE_MARKER); if ((isLanguageConsole == null || !isLanguageConsole) && !bashFile.hasShebangLine()) { return new ProblemDescriptor[]{ manager.createProblemDescriptor(checkedFile, "Add shebang line", new AddShebangQuickfix(), ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly) }; } } return null; }
private String description(List<PsiErrorElement> errors) { StringBuilder builder = new StringBuilder(); builder.append("\n## File: " + getFile().getName()); builder.append(", Errors: " + errors.size()); for (PsiErrorElement error : errors) { builder.append("\n\t").append(error.getErrorDescription()); builder.append(": '").append(error.getText()).append("'").append(", line ").append(BashPsiUtils.getElementLineNumber(error)); //builder.append(", column ").append(error.getTgetTextOffset()); } builder.append("\n\n"); return builder.toString(); } }
int referenceLevel = preferNeigbourhood && (referenceElement != null) ? BashPsiUtils.blockNestingLevel(referenceElement) : 0; if (BashPsiUtils.isInjectedElement(e)) {
public boolean execute(@NotNull PsiElement element, @NotNull ResolveState resolveState) { if (element instanceof BashFunctionDef) { BashFunctionDef funcDef = (BashFunctionDef) element; if (symboleName.equals(funcDef.getName())) { storeResult(element, BashPsiUtils.blockNestingLevel(funcDef)); return ignoreExecuteResult; } } return true; }
@Nullable public List<PsiComment> findAttachedComment() { return BashPsiUtils.findDocumentationElementComments(this); }
/** * Returns the depth in blocks this element has in the tree. * * @param element The element to lookup * @return The depth measured in blocks, 0 if it's at the top level */ public static int blockNestingLevel(PsiElement element) { int depth = 0; PsiElement current = findEnclosingBlock(element); while (current != null) { depth++; current = findEnclosingBlock(current); } return depth; }
/** * Returns the commands of file which include the other file. * * @param file * @return The list of files which are included in the first file */ public static Set<PsiFile> findIncludedFiles(PsiFile file, boolean followNestedFiles) { Set<PsiFile> files = Sets.newLinkedHashSet(); collectIncludedFiles(file, files, followNestedFiles); return files; }