コード例 #1
0
        /// <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);
                }
            }
        }
コード例 #2
0
        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();
        }
コード例 #3
0
        /// <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);
                }
            }
        }
コード例 #4
0
        /// <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);
                }
            }
        }
コード例 #5
0
        /// <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);
                }
            }
        }
コード例 #6
0
        /// <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);
        }
コード例 #9
0
        /// <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);
                }
            }
        }
コード例 #10
0
        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));
            }
        }
コード例 #13
0
        /// <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);
            }));
        }
コード例 #15
0
            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;
                }
                }
            }