/// <summary> /// Do not place regions within elements. /// </summary> /// <param name="node"> /// The node to process. /// </param> private static void DoNotPlaceRegionsWithinElements(ITreeNode node) { for (ITreeNode currentNode = node; currentNode != null; currentNode = currentNode.NextSibling) { if (currentNode is ITokenNode) { ITokenNode tokenNode = currentNode as ITokenNode; if (tokenNode.GetTokenType() == CSharpTokenType.PP_START_REGION) { IStartRegion startRegionNode = tokenNode.GetContainingNode <IStartRegion>(true); if (startRegionNode != null) { if (startRegionNode.Parent is IBlock) { // we're in a block so remove the end and start region IEndRegion endRegionNode = startRegionNode.EndRegion; using (WriteLockCookie.Create(true)) { LowLevelModificationUtil.DeleteChild(endRegionNode); LowLevelModificationUtil.DeleteChild(startRegionNode); } } } } } if (currentNode.FirstChild != null) { DoNotPlaceRegionsWithinElements(currentNode.FirstChild); } } }
private void ChangeDocumentation(string text) { text = text.Trim('\r', '\n'); if (!string.IsNullOrEmpty(text)) { var newNode = GetElementFactory().CreateDocCommentBlock(text); if (IsCreated) { LowLevelModificationUtil.ReplaceChildRange(Node, Node, newNode); } else { LowLevelModificationUtil.AddChildBefore(AnalyzeUnit.Node.FirstChild, newNode); } newNode.FormatNode(); Node = newNode; } else if (Node != null) { LowLevelModificationUtil.DeleteChild(Node); Node = null; } Update(); }
/// <summary> /// Code must not contain empty statements. /// </summary> /// <param name="node"> /// The node to process. /// </param> private static void CodeMustNotContainEmptyStatements(ITreeNode node) { for (ITreeNode currentNode = node; currentNode != null; currentNode = currentNode.NextSibling) { if (currentNode is ITokenNode) { ITokenNode tokenNode = currentNode as ITokenNode; if (tokenNode.GetTokenType() == CSharpTokenType.SEMICOLON && !(tokenNode.Parent is IForStatement)) { ITokenNode nextNonWhitespaceToken = Utils.GetFirstNonWhitespaceTokenToRight(tokenNode); while (nextNonWhitespaceToken.GetTokenType() == CSharpTokenType.SEMICOLON) { using (WriteLockCookie.Create(true)) { if (nextNonWhitespaceToken.GetNextToken().GetTokenType() == CSharpTokenType.WHITE_SPACE) { LowLevelModificationUtil.DeleteChild(nextNonWhitespaceToken.GetNextToken()); } // remove the spare semi colon LowLevelModificationUtil.DeleteChild(nextNonWhitespaceToken); nextNonWhitespaceToken = Utils.GetFirstNonWhitespaceTokenToRight(tokenNode); } } } } if (currentNode.FirstChild != null) { CodeMustNotContainEmptyStatements(currentNode.FirstChild); } } }
/// <summary> /// The code must not contain multiple whitespace in a row. /// </summary> /// <param name="node"> /// The node. /// </param> public void CodeMustNotContainMultipleWhitespaceInARow(ITreeNode node) { for (ITreeNode currentNode = node; currentNode != null; currentNode = currentNode.NextSibling) { if (currentNode is ITokenNode) { ITokenNode currentToken = currentNode as ITokenNode; ITokenNode previousToken = currentToken.GetPrevToken(); if (previousToken != null) { if (currentToken.GetTokenType() == CSharpTokenType.WHITE_SPACE && previousToken.GetTokenType() == CSharpTokenType.WHITE_SPACE) { using (WriteLockCookie.Create(true)) { LowLevelModificationUtil.DeleteChild(currentToken); } } } } if (currentNode.FirstChild != null) { this.CodeMustNotContainMultipleWhitespaceInARow(currentNode.FirstChild); } } }
/// <summary> /// Preprocessor keywords must not be preceded by space. /// </summary> /// <param name="node"> /// The node to use. /// </param> public void PreprocessorKeywordsMustNotBePrecededBySpace(ITreeNode node) { for (ITreeNode currentNode = node; currentNode != null; currentNode = currentNode.NextSibling) { if (currentNode is IPreprocessorDirective) { IPreprocessorDirective preprocessorDirectiveNode = currentNode as IPreprocessorDirective; TreeOffset directiveTokenNodeOffset = preprocessorDirectiveNode.Directive.GetTreeStartOffset(); TreeOffset numberSignTokenNodeOffset = preprocessorDirectiveNode.NumberSign.GetTreeStartOffset(); if (directiveTokenNodeOffset - 1 != numberSignTokenNodeOffset) { // There is a gap between them ITokenNode tokenNode = preprocessorDirectiveNode.NumberSign; ITokenNode nextToken = tokenNode.GetNextToken(); using (WriteLockCookie.Create(true)) { // remove the whitespace or new line LowLevelModificationUtil.DeleteChild(nextToken); } } } if (currentNode.FirstChild != null) { this.PreprocessorKeywordsMustNotBePrecededBySpace(currentNode.FirstChild); } } }
/// <summary> /// Negative and positive signs must be spaced correctly. /// </summary> /// <param name="node"> /// The node to use. /// </param> /// <param name="tokenToCheck"> /// The token to check. /// </param> public void NegativeAndPositiveSignsMustBeSpacedCorrectly(ITreeNode node, TokenNodeType tokenToCheck) { for (ITreeNode currentNode = node; currentNode != null; currentNode = currentNode.NextSibling) { if (currentNode is ITokenNode) { ITokenNode tokenNode = currentNode as ITokenNode; if (tokenNode.GetTokenType() == tokenToCheck) { if (tokenNode.Parent is IOperatorExpression && !(tokenNode.Parent is IAdditiveExpression)) { ITokenNode nextToken = tokenNode.GetNextToken(); if (nextToken.IsWhitespace()) { using (WriteLockCookie.Create(true)) { // remove the whitespace or new line LowLevelModificationUtil.DeleteChild(nextToken); } } } } } if (currentNode.FirstChild != null) { this.NegativeAndPositiveSignsMustBeSpacedCorrectly(currentNode.FirstChild, tokenToCheck); } } }
public override CSharpPostfixExpressionContext FixExpression(CSharpPostfixExpressionContext context) { var psiServices = Reference.GetPsiServices(); var expressionRange = ExecutionContext.GetDocumentRange(context.Expression); var referenceRange = ExecutionContext.GetDocumentRange(Reference); var textWithReference = expressionRange.SetEndTo(referenceRange.TextRange.EndOffset).GetText(); var indexOfReferenceDot = textWithReference.LastIndexOf('.'); if (indexOfReferenceDot <= 0) { return(context); } var realReferenceRange = referenceRange.SetStartTo(expressionRange.TextRange.StartOffset + indexOfReferenceDot); var transactionManager = psiServices.Transactions.DocumentTransactionManager; var document = expressionRange.Document; // todo: make sure this is not in undo stack! using (transactionManager.CreateTransactionCookie(DefaultAction.Commit, FixCommandName)) { document.ReplaceText(realReferenceRange.TextRange, ")"); document.InsertText(expressionRange.TextRange.StartOffset, "unchecked("); } //using (psiServices.Solution.CreateTransactionCookie(DefaultAction.Commit, FixCommandName, NullProgressIndicator.Instance)) //{ // //} psiServices.Files.CommitAllDocuments(); var uncheckedExpression = TextControlToPsi.GetElement <IUncheckedExpression>(psiServices.Solution, document, expressionRange.TextRange.StartOffset + 1); if (uncheckedExpression == null) { return(context); } var operand = uncheckedExpression.Operand; psiServices.Transactions.Execute(FixCommandName, () => { LowLevelModificationUtil.DeleteChild(operand); LowLevelModificationUtil.ReplaceChildRange(uncheckedExpression, uncheckedExpression, operand); }); Assertion.Assert(operand.IsPhysical(), "operand.IsPhysical()"); return(new CSharpPostfixExpressionContext(this, operand)); }
public override PrefixExpressionContext FixExpression(PrefixExpressionContext context) { var expression = context.Expression; if (expression.Contains(Reference)) // x is T.bar => x is T { expression.GetPsiServices().DoTransaction(FixCommandName, () => { var referenceName = (IReferenceName)Reference; var qualifier = referenceName.Qualifier; LowLevelModificationUtil.DeleteChild(qualifier); // remove first return(referenceName.ReplaceBy(qualifier)); }); } return(context); }
/// <summary> /// Delete child range. /// </summary> /// <param name="first"> /// The first token to delete. /// </param> /// <param name="last"> /// The last token to delete. /// </param> private static void DeleteChildRange(ITokenNode first, ITokenNode last) { using (WriteLockCookie.Create(true)) { List <ITokenNode> a = new List <ITokenNode>(); ITokenNode tokenNodeToStopAt = last.GetNextToken(); for (ITokenNode foundToken = first; foundToken != tokenNodeToStopAt; foundToken = foundToken.GetNextToken()) { a.Add(foundToken); } foreach (ITokenNode tokenNode in a) { LowLevelModificationUtil.DeleteChild(tokenNode); } } }
public override CSharpPostfixExpressionContext FixExpression(CSharpPostfixExpressionContext context) { var expression = context.Expression; if (expression.Contains(Reference)) // x is T.bar => x is T { var psiServices = expression.GetPsiServices(); psiServices.Transactions.Execute(FixCommandName, () => { var referenceName = (IReferenceName)Reference; var qualifier = referenceName.Qualifier; LowLevelModificationUtil.DeleteChild(qualifier); // remove first LowLevelModificationUtil.ReplaceChildRange(referenceName, referenceName, qualifier); }); } return(context); }
public override CSharpPostfixExpressionContext FixExpression(CSharpPostfixExpressionContext context) { var referenceExpression = (IReferenceExpression)Reference; var expression = context.Expression; if (expression.Parent == referenceExpression) // foo.bar => foo { var psiServices = expression.GetPsiServices(); psiServices.Transactions.Execute(FixCommandName, () => { LowLevelModificationUtil.DeleteChild(expression); LowLevelModificationUtil.ReplaceChildRange(referenceExpression, referenceExpression, expression); }); Assertion.Assert(expression.IsPhysical(), "expression.IsPhysical()"); return(new CSharpPostfixExpressionContext(this, expression)); } if (expression.Contains(referenceExpression)) // boo > foo.bar => boo > foo { var qualifier = referenceExpression.QualifierExpression; var psiServices = expression.GetPsiServices(); psiServices.Transactions.Execute(FixCommandName, () => { LowLevelModificationUtil.DeleteChild(qualifier); LowLevelModificationUtil.ReplaceChildRange(referenceExpression, referenceExpression, qualifier); }); Assertion.AssertNotNull(qualifier, "qualifier != null"); Assertion.Assert(qualifier.IsPhysical(), "qualifier.IsPhysical()"); } return(context); }
public override void Accept( ITextControl textControl, DocumentRange nameRange, LookupItemInsertType insertType, Suffix suffix, ISolution solution, bool keepCaretStill) { var psiServices = solution.GetPsiServices(); var updateMethodDeclaration = TextControlToPsi.GetElement <IMethodDeclaration>(solution, textControl); if (!Info.ShouldGenerateMethod) { UpdateExistingMethod(updateMethodDeclaration, psiServices); return; } var isCoroutine = updateMethodDeclaration?.TypeUsage is IUserTypeUsage userTypeUsage && userTypeUsage.ScalarTypeName?.ShortName == "IEnumerator"; var fixedNameRange = nameRange.SetStartTo(Info.MemberReplaceRanges.InsertRange.StartOffset); var memberRange = Info.MemberReplaceRanges.GetAcceptRange(fixedNameRange, insertType); // Insert a dummy method declaration, as text, which means the PSI is reparsed. This will remove empty type // usages and merge leading attributes into a method declaration, such that we can copy them and replace // them once the declared element has expanded. This also fixes up the case where the type usage picks up // the attribute of the next code construct as an array specifier. E.g. `OnAni{caret} [SerializeField]` using (WriteLockCookie.Create()) { textControl.Document.ReplaceText(memberRange, "void Foo(){}"); } psiServices.Files.CommitAllDocuments(); var methodDeclaration = TextControlToPsi.GetElement <IMethodDeclaration>(solution, textControl); if (methodDeclaration == null) { return; } var methodDeclarationCopy = methodDeclaration.Copy(); var nodesBeforeCopyRange = NodesBeforeMethodHeader(methodDeclarationCopy); using (new PsiTransactionCookie(psiServices, DefaultAction.Commit, "RemoveInsertedDeclaration")) using (WriteLockCookie.Create()) { LowLevelModificationUtil.DeleteChild(methodDeclaration); } var classDeclaration = TextControlToPsi.GetElement <IClassLikeDeclaration>(solution, textControl); Assertion.AssertNotNull(classDeclaration, "classDeclaration != null"); var factory = CSharpElementFactory.GetInstance(classDeclaration); GenerateCodeWorkflowBase.ExecuteNonInteractive( GeneratorUnityKinds.UnityEventFunctions, solution, textControl, methodDeclaration.Language, configureContext: context => { // Note that the generated code will use the access rights, if specified. However, if they haven't // been specified (NONE) or they are the default for methods (PRIVATE), the generated code will be // whatever the current code style setting is - implicit or explicit var knownTypesCache = solution.GetComponent <KnownTypesCache>(); var declaredElement = myEventFunction.CreateDeclaration(factory, knownTypesCache, classDeclaration, myAccessRights, makeCoroutine: isCoroutine) .DeclaredElement.NotNull("declaredElement != null"); context.InputElements.Clear(); context.InputElements.Add(new GeneratorDeclaredElement(declaredElement)); }, onCompleted: context => { if (nodesBeforeCopyRange.IsEmpty) { return; } foreach (var outputElement in context.OutputElements) { if (outputElement is GeneratorDeclarationElement declarationElement) { using (new PsiTransactionCookie(psiServices, DefaultAction.Commit, "BringBackAttributes")) using (WriteLockCookie.Create()) { var newDeclaration = declarationElement.Declaration; ModificationUtil.AddChildRangeAfter(newDeclaration, anchor: null, nodesBeforeCopyRange); } return; } } }); ITreeRange NodesBeforeMethodHeader(IMethodDeclaration declaration) { var firstNode = declaration.ModifiersList ?? declaration.TypeUsage as ITreeNode; var smthBeforeTypeUsage = firstNode?.PrevSibling; if (smthBeforeTypeUsage == null) { return(TreeRange.Empty); } return(new TreeRange(declaration.FirstChild, smthBeforeTypeUsage)); } }
/// <summary> /// Fixes the spacing of the using directives in a given file or namespace block /// to match the specified configuration. (The directives must already be in /// the correct order.) /// </summary> /// <param name="holder">The file or namespace block in which to fix the spacing /// of using directives (if any are present).</param> /// <param name="configuration">The configuration determining the correct spacing.</param> public static void FixSpacing( ICSharpTypeAndNamespaceHolderDeclaration holder, OrderUsingsConfiguration configuration) { // The reordering proceeds one item at a time, so we just keep reapplying it // until there's nothing left to do. // To avoid hanging VS in the event that an error in the logic causes the // sequence of modifications not to terminate, we ensure we don't try to // apply more changes than there are either using directives or blank // lines in the usings list. int tries = 0; int itemCount = 0; while (tries == 0 || tries <= itemCount) { List <UsingDirectiveOrSpace> items = ImportReader.ReadImports(holder); if (items == null) { return; } itemCount = items.Count; List <UsingDirective> imports; List <List <UsingDirective> > requiredOrderByGroups; ImportInspector.FlattenImportsAndDetermineOrderAndSpacing( configuration, items, out imports, out requiredOrderByGroups); SpaceChange nextChange = ImportInspector.GetNextSpacingModification(requiredOrderByGroups, items); if (nextChange != null) { IUsingDirective usingBeforeSpace = holder.Imports[nextChange.Index - 1]; if (nextChange.ShouldInsert) { using (WriteLockCookie.Create()) { var newLineText = new StringBuffer("\r\n"); LeafElementBase newLine = TreeElementFactory.CreateLeafElement( CSharpTokenType.NEW_LINE, newLineText, 0, newLineText.Length); LowLevelModificationUtil.AddChildAfter(usingBeforeSpace, newLine); } } else { var syb = usingBeforeSpace.NextSibling; for (; syb != null && !(syb is IUsingDirective); syb = syb.NextSibling) { if (syb.NodeType == CSharpTokenType.NEW_LINE) { LowLevelModificationUtil.DeleteChild(syb); } } } } else { break; } tries += 1; } }
protected override ITreeNode ExpandPostfix(PostfixExpressionContext context) { var csharpContext = (CSharpPostfixExpressionContext)context; var psiModule = csharpContext.PostfixContext.PsiModule; var psiServices = psiModule.GetPsiServices(); var factory = CSharpElementFactory.GetInstance(psiModule); var targetStatement = csharpContext.GetContainingStatement(); var expressionRange = csharpContext.Expression.GetDocumentRange(); // Razor issue - hard to convert expression to statement if (!targetStatement.GetDocumentRange().IsValid()) { var newStatement = psiServices.DoTransaction(ExpandCommandName, () => { // todo: pass original context? var expression = csharpContext.Expression.GetOperandThroughParenthesis().NotNull(); return(CreateStatement(factory, expression)); }); var razorStatement = RazorUtil.FixExpressionToStatement(expressionRange, psiServices); if (razorStatement != null) { return(psiServices.DoTransaction(ExpandCommandName, () => { var statement = razorStatement.ReplaceBy(newStatement); // force Razor's bracing style var languageService = statement.Language.LanguageService().NotNull(); var formatter = languageService.CodeFormatter.NotNull(); formatter.Format(statement, CodeFormatProfile.SOFT, NullProgressIndicator.Instance); return statement; })); } Logger.Fail("Failed to resolve target statement to replace"); return(null); } return(psiServices.DoTransaction(ExpandCommandName, () => { var expression = csharpContext.Expression.GetOperandThroughParenthesis().NotNull(); var newStatement = CreateStatement(factory, expression); Assertion.AssertNotNull(targetStatement, "targetStatement != null"); Assertion.Assert(targetStatement.IsPhysical(), "targetStatement.IsPhysical()"); // Sometimes statements produced by templates are unfinished (for example, because of // parentheses insertion mode in R#), so the created statements has error element at and, // prefixed with single-characted whitespace. We remove this whitespace here: var errorElement = newStatement.LastChild as IErrorElement; if (errorElement != null) { var whitespaceNode = errorElement.PrevSibling as IWhitespaceNode; if (whitespaceNode != null && !whitespaceNode.IsNewLine && whitespaceNode.GetText() == " ") { using (WriteLockCookie.Create(newStatement.IsPhysical())) { LowLevelModificationUtil.DeleteChild(whitespaceNode); } } } return targetStatement.ReplaceBy(newStatement); })); }
private void PlaceCaretAfterCompletion( [NotNull] ITextControl textControl, [NotNull] IReferenceExpression referenceExpression, int existingArgumentsCount, LookupItemInsertType insertType) { var referenceRange = referenceExpression.GetDocumentRange(); textControl.Caret.MoveTo(referenceRange.TextRange.EndOffset, CaretVisualPlacement.DontScrollIfVisible); var invocationExpression = InvocationExpressionNavigator.GetByInvokedExpression(referenceExpression); if (invocationExpression == null) { return; } var invocationRange = invocationExpression.GetDocumentRange(); textControl.Caret.MoveTo(invocationRange.TextRange.EndOffset, CaretVisualPlacement.DontScrollIfVisible); var settingsStore = referenceExpression.GetSettingsStore(); var parenthesesInsertType = settingsStore.GetValue(CodeCompletionSettingsAccessor.ParenthesesInsertType); var hasMoreParametersToPass = HasMoreParametersToPass(existingArgumentsCount); switch (parenthesesInsertType) { case ParenthesesInsertType.Both: { if (hasMoreParametersToPass) { var rightPar = invocationExpression.RPar; if (rightPar != null) { var rightParRange = rightPar.GetDocumentRange().TextRange; textControl.Caret.MoveTo(rightParRange.StartOffset, CaretVisualPlacement.DontScrollIfVisible); } } break; } case ParenthesesInsertType.Left: case ParenthesesInsertType.None: { // if in insert mode - drop right par and set caret to it's start offest if (insertType == LookupItemInsertType.Insert) { var rightPar = invocationExpression.RPar; if (rightPar != null) { var rightParRange = rightPar.GetDocumentRange().TextRange; invocationExpression.GetPsiServices().Transactions.Execute( commandName: typeof(StaticMethodBehavior).FullName, handler: () => { using (WriteLockCookie.Create()) LowLevelModificationUtil.DeleteChild(rightPar); }); textControl.Caret.MoveTo(rightParRange.StartOffset, CaretVisualPlacement.DontScrollIfVisible); } } break; } } }