// protected override ICSharpStatement DecorateStatement(CSharpElementFactory factory, // ICSharpStatement statement) // { // var info = (ValidatePostfixTemplateInfo) Info; // var type = TypeFactory.CreateType(info.ValidatorTypes.First()); // var newStatement = factory.CreateStatement("var validator = new $0();", type); // var statementsOwner = StatementsOwnerNavigator.GetByStatement(statement); // return statementsOwner.AddStatementBefore(newStatement, statement); // } protected override void AfterComplete(ITextControl textControl, ICSharpStatement statement, Suffix suffix) { var expressionMarker = statement.GetDocumentRange().CreateRangeMarker(); var solution = statement.GetSolution(); ExecuteRefactoring(textControl, statement.Descendants <IObjectCreationExpression>().First(), () => { var documentRange = expressionMarker.DocumentRange; if (!documentRange.IsValid()) { return; } solution.GetPsiServices().Files.CommitAllDocuments(); var element = TextControlToPsi.GetElement <ICSharpStatement>(solution, documentRange.EndOffset); if (element == null) { return; } textControl.Caret.MoveTo(element.GetDocumentRange().TextRange.EndOffset, CaretVisualPlacement.DontScrollIfVisible); }); // base.AfterComplete(textControl, statement, suffix); }
public override ISpecificCodeCompletionContext GetCompletionContext(CodeCompletionContext context) { var relatedText = string.Empty; var nodeUnderCursor = TextControlToPsi.GetElement<ITreeNode>(context.Solution, context.TextControl); var interestingNode = GetInterestingNode(nodeUnderCursor); if (interestingNode == null) return null; var ranges = GetTextLookupRanges(context, nodeUnderCursor.GetDocumentRange()); if (interestingNode is GherkinStep step) { var stepTextRange = step.GetStepTextRange(); if (IsCursorBeforeNode(context, stepTextRange)) return null; relatedText = step.GetStepTextBeforeCaret(context.CaretDocumentOffset); if (IsCursorAfterNode(context, stepTextRange)) { stepTextRange = stepTextRange.ExtendRight(context.CaretDocumentOffset.Offset - stepTextRange.EndOffset.Offset); relatedText += " "; } var replaceRange = stepTextRange; var insertRange = stepTextRange.SetEndTo(context.SelectedRange.EndOffset); ranges = new TextLookupRanges(insertRange, replaceRange); } return new GherkinSpecificCodeCompletionContext(context, ranges, interestingNode, relatedText); }
private ITreeNode FindCurrentTreeNode(IDocument document) { var textControl = _textControlManager.TextControls.FirstOrDefault( tc => tc.Document.Moniker.Equals(document.Moniker)); return(textControl == null ? null : TextControlToPsi.GetElement <ITreeNode>(_solution, textControl)); }
private ITreeNode GetTreeNode([NotNull] ITextControl textControl) { ITreeNode treeNode = null; if (ReentrancyGuard.Current.CanExecuteNow) { ReadLockCookie.GuardedExecute( () => { treeNode = TextControlToPsi.GetElement <ITreeNode>(_solution, textControl); }); } return(treeNode); }
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)); }
private ITreeNode GetTreeNode([NotNull] ITextControl textControl) { var treeNode = TextControlToPsi.GetElement <ITreeNode>(_solution, textControl); if (treeNode.IsWhitespaceToken() && treeNode.NextSibling != null) { treeNode = treeNode.NextSibling; } if (treeNode != null && CSharpTokenType.DOT == treeNode.GetTokenType()) { treeNode = treeNode.NextSibling; } return(treeNode); }
protected override void AfterComplete(ITextControl textControl, ICSharpExpression expression) { var expressionRange = expression.GetDocumentRange(); var expressionMarker = expressionRange.CreateRangeMarker(); var solution = expression.GetSolution(); ExecuteRefactoring(textControl, expression, () => { var referenceRange = expressionMarker.Range; if (!referenceRange.IsValid) { return; } var reference = TextControlToPsi.GetElement <IReferenceExpression>(solution, textControl.Document, referenceRange.EndOffset); if (reference != null && reference.QualifierExpression == null) { var endOffset = reference.GetDocumentRange().TextRange.EndOffset; textControl.Caret.MoveTo(endOffset, CaretVisualPlacement.DontScrollIfVisible); } }); }
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)); } }
public override void Accept(ITextControl textControl, TextRange nameRange, LookupItemInsertType lookupItemInsertType, Suffix suffix, ISolution solution, bool keepCaretStill) { var rangeMarker = nameRange.CreateRangeMarkerWithMappingToDocument(textControl.Document); var identifierNode = TextControlToPsi.GetElement <ITreeNode>(solution, textControl); var psiServices = solution.GetPsiServices(); if (identifierNode != null) { IErrorElement errorElement = null; ITreeNode usage = identifierNode.GetContainingNode <IUserTypeUsage>(); if (usage != null) { errorElement = usage.NextSibling as IErrorElement; } else { usage = identifierNode.PrevSibling; while (usage != null && !(usage is ITypeUsage)) { usage = usage.PrevSibling; } errorElement = identifierNode.NextSibling as IErrorElement; } using (var cookie = new PsiTransactionCookie(psiServices, DefaultAction.Rollback, "RemoveIdentifier")) using (new DisableCodeFormatter()) { using (WriteLockCookie.Create()) { ModificationUtil.DeleteChild(identifierNode); if (usage != null) { ModificationUtil.DeleteChild(usage); } if (errorElement != null) { ModificationUtil.DeleteChild(errorElement); } } cookie.Commit(); } } using (WriteLockCookie.Create()) { textControl.Document.InsertText(rangeMarker.Range.StartOffset, "void Foo(){}"); } psiServices.Files.CommitAllDocuments(); var methodDeclaration = TextControlToPsi.GetElement <IMethodDeclaration>(solution, textControl); if (methodDeclaration == null) { return; } var insertionIndex = methodDeclaration.GetTreeStartOffset().Offset; string attributeText = null; var attributeList = methodDeclaration.FirstChild as IAttributeSectionList; if (attributeList != null) { attributeText = attributeList.GetText(); var treeNode = attributeList.NextSibling; while (treeNode is IWhitespaceNode) { attributeText += treeNode.GetText(); treeNode = treeNode.NextSibling; } } using (var cookie = new PsiTransactionCookie(psiServices, DefaultAction.Rollback, "RemoveInsertedDeclaration")) using (new DisableCodeFormatter()) { using (WriteLockCookie.Create()) ModificationUtil.DeleteChild(methodDeclaration); cookie.Commit(); } // Get the UniteMessages generator to actually insert the methods GenerateCodeWorkflowBase.ExecuteNonInteractive( GeneratorUnityKinds.UnityMessages, solution, textControl, methodDeclaration.Language, configureContext: context => { var inputElements = from e in context.ProvidedElements.Cast <GeneratorDeclaredElement <IMethod> >() where myMessage.Match(e.DeclaredElement) select e; context.InputElements.Clear(); context.InputElements.AddRange(inputElements); }); if (!string.IsNullOrEmpty(attributeText)) { using (WriteLockCookie.Create()) textControl.Document.InsertText(insertionIndex, attributeText); } }
private ITreeNode FindCurrentTreeNode() { var textControl = _textControlManager.FocusedTextControl.Value; return(textControl == null ? null : TextControlToPsi.GetElement <ITreeNode>(_solution, textControl)); }
private void Accept(ITextControl textControl, IRangeMarker rangeMarker, ISolution solution) { var psiServices = solution.GetPsiServices(); // Get the node at the caret. This will be the identifier var identifierNode = TextControlToPsi.GetElement <ITreeNode>(solution, textControl); if (identifierNode == null) { return; } // Delete the half completed identifier node. Also delete any explicitly entered // return type, as our declared element will create one anyway if (!(identifierNode.GetPreviousMeaningfulSibling() is ITypeUsage typeUsage)) { // E.g. `void OnAnim{caret} [SerializeField]...` This is parsed as a field with an array specifier var fieldDeclaration = identifierNode.GetContainingNode <IFieldDeclaration>(); typeUsage = fieldDeclaration?.GetPreviousMeaningfulSibling() as ITypeUsage; } using (var cookie = new PsiTransactionCookie(psiServices, DefaultAction.Rollback, "RemoveIdentifier")) using (new DisableCodeFormatter()) { using (WriteLockCookie.Create()) { ModificationUtil.DeleteChild(identifierNode); if (typeUsage != null) { ModificationUtil.DeleteChild(typeUsage); } } cookie.Commit(); } // 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.InsertText(rangeMarker.DocumentRange.StartOffset, "void Foo(){}"); psiServices.Files.CommitAllDocuments(); var methodDeclaration = TextControlToPsi.GetElement <IMethodDeclaration>(solution, textControl); if (methodDeclaration == null) { return; } var attributeList = methodDeclaration.FirstChild as IAttributeSectionList; using (var cookie = new PsiTransactionCookie(psiServices, DefaultAction.Rollback, "RemoveInsertedDeclaration")) using (new DisableCodeFormatter()) { using (WriteLockCookie.Create()) ModificationUtil.DeleteChild(methodDeclaration); cookie.Commit(); } // Get the UnityEventFunction generator to actually insert the methods GenerateCodeWorkflowBase.ExecuteNonInteractive( GeneratorUnityKinds.UnityEventFunctions, solution, textControl, identifierNode.Language, configureContext: context => { var inputElements = from e in context.ProvidedElements.Cast <GeneratorDeclaredElement <IMethod> >() where myEventFunction.Match(e.DeclaredElement) != MethodSignatureMatch.NoMatch select e; context.InputElements.Clear(); context.InputElements.AddRange(inputElements); }); if (attributeList != null) { methodDeclaration = TextControlToPsi.GetElement <IMethodDeclaration>(solution, textControl); if (methodDeclaration != null) { using (var transactionCookie = new PsiTransactionCookie(psiServices, DefaultAction.Rollback, "InsertAttributes")) { methodDeclaration.SetAttributeSectionList(attributeList); transactionCookie.Commit(); } } } }
private static AfterCompletionHandler BakeAfterComplete([NotNull] ILookupItem lookupItem, [NotNull] ISolution solution, int argumentsCount) { // sorry, ugly as f**k :( return((ITextControl textControl, ref TextRange range, ref TextRange decoration, TailType tailType, ref Suffix suffix, ref IRangeMarker caretMarker) => { var psiServices = solution.GetPsiServices(); psiServices.CommitAllDocuments(); var allMethods = GetAllTargetMethods(lookupItem); if (allMethods.Count == 0) { return; } var reference = TextControlToPsi.GetElement <IReferenceExpression>(solution, textControl.Document, range.StartOffset); if (reference == null) { return; } var decorationText = textControl.Document.GetText(decoration); var decorationRange = new DocumentRange(textControl.Document, decoration); var hasMoreParameters = HasMoreParameters(allMethods, argumentsCount); if (!hasMoreParameters) // put caret 'foo(arg){here};' { caretMarker = decorationRange.EndOffsetRange().CreateRangeMarker(); } else if (argumentsCount > 0) { var parenthesisCloseIndex = decorationText.LastIndexOf(')'); if (parenthesisCloseIndex >= 0) { var delta = decoration.Length - parenthesisCloseIndex; caretMarker = decorationRange.EndOffsetRange().Shift(-delta).CreateRangeMarker(); } } var qualifierExpression = reference.QualifierExpression.NotNull("qualifierExpression != null"); var referencePointer = reference.CreateTreeElementPointer(); var qualifierText = InsertQualifierAsArgument( qualifierExpression, allMethods, argumentsCount, textControl, decoration, decorationText); // TODO: mmm? if (!hasMoreParameters && !decorationText.EndsWith(")", StringComparison.Ordinal)) { caretMarker = caretMarker.DocumentRange.Shift(+qualifierText.Length).CreateRangeMarker(); } // replace qualifier with type (predefined/user type) var ownerType = allMethods[0].GetContainingType().NotNull("ownerType != null"); FixQualifierExpression(textControl, qualifierExpression, ownerType); psiServices.CommitAllDocuments(); var newReference = referencePointer.GetTreeNode(); if (newReference != null) { var keyword = CSharpTypeFactory.GetTypeKeyword(ownerType.GetClrName()); if (keyword == null) // bind user type { var newQualifier = (IReferenceExpression)newReference.QualifierExpression; if (newQualifier != null) { var elementInstance = lookupItem.GetDeclaredElement().NotNull("elementInstance != null"); newQualifier.Reference.BindTo(ownerType, elementInstance.Substitution); } range = newReference.NameIdentifier.GetDocumentRange().TextRange; decoration = TextRange.InvalidRange; } // show parameter info when needed if (hasMoreParameters) { var factory = solution.GetComponent <LookupItemsOwnerFactory>(); var lookupItemsOwner = factory.CreateLookupItemsOwner(textControl); LookupUtil.ShowParameterInfo(solution, textControl, lookupItemsOwner); } } TipsManager.Instance.FeatureIsUsed( "Plugin.ControlFlow.PostfixTemplates.<static>", textControl.Document, solution); }); }
public void Accept(ITextControl textControl, TextRange nameRange, LookupItemInsertType lookupItemInsertType, Suffix suffix, ISolution solution, bool keepCaretStill) { /* PLEASE DO NOT OBSERVE THIS */ string text; if (lookupItemInsertType == LookupItemInsertType.Insert) { if (Info.InsertText.IndexOf(')') == -1) { Info.InsertText += ")"; } text = Info.Text + Info.InsertText; textControl.Document.ReplaceText(Info.Ranges.InsertRange.JoinRight(nameRange), text); } else { if (Info.ReplaceText.IndexOf(')') == -1) { Info.ReplaceText += ")"; } text = Info.Text + Info.ReplaceText; textControl.Document.ReplaceText(Info.Ranges.ReplaceRange.JoinRight(nameRange), text); } var tailType = Info.TailType; if (tailType != null) { LookupUtil.InsertTailType(textControl, nameRange.StartOffset + text.Length, tailType, solution, emulateTypingOfSpace: false); } /* PLEASE DO NOT OBSERVE THIS */ var psiServices = solution.GetPsiServices(); psiServices.Files.CommitAllDocuments(); var referenceExpression = TextControlToPsi.GetElement <IReferenceExpression>(solution, textControl.Document, nameRange.StartOffset); if (referenceExpression == null || myMethods.Count == 0) { return; } var existingArgumentsCount = GetExistingArgumentsCount(referenceExpression); var referencePointer = referenceExpression.CreateTreeElementPointer(); InsertQualifierAsArgument(referenceExpression, existingArgumentsCount, textControl); psiServices.Files.CommitAllDocuments(); var reference1 = referencePointer.GetTreeNode(); if (reference1 != null) { var textRange = reference1.NameIdentifier.GetDocumentRange().TextRange; var pointers = Info.DeclaredElementPointers.ToList(); #if RESHARPER92 var caretPositionMarker = RangeMarker.InvalidMarker; LookupUtil.BindRange(solution, textControl, textRange, pointers, reference1.Language, ref caretPositionMarker); #else LookupUtil.BindRange(solution, textControl, textRange, pointers, reference1.Language); #endif psiServices.Files.CommitAllDocuments(); var reference3 = referencePointer.GetTreeNode(); if (reference3 != null) { ApplyQualifierCodeStyle(reference3); psiServices.Files.CommitAllDocuments(); var reference2 = referencePointer.GetTreeNode(); if (reference2 != null) { PlaceCaretAfterCompletion(textControl, reference2, existingArgumentsCount, lookupItemInsertType); } } } }
private void Accept(ITextControl textControl, DocumentRange nameRange, ISolution solution) { var psiServices = solution.GetPsiServices(); // Get the node at the caret. This will be the identifier var identifierNode = TextControlToPsi.GetElement <ITreeNode>(solution, textControl) as IIdentifier; if (identifierNode == null) { return; } var methodDeclaration = TextControlToPsi.GetElement <IMethodDeclaration>(solution, textControl); if (UpdateExistingMethod(methodDeclaration, psiServices)) { return; } // Delete the half completed identifier node. Also delete any explicitly entered return type, as our // declared element will create one anyway if (!(identifierNode.GetPreviousMeaningfulSibling() is ITypeUsage typeUsage)) { // E.g. `void OnAnim{caret} [SerializeField]...` This is parsed as a field with an array specifier var fieldDeclaration = identifierNode.GetContainingNode <IFieldDeclaration>(); typeUsage = fieldDeclaration?.GetPreviousMeaningfulSibling() as ITypeUsage; } var parameterListStart = methodDeclaration?.LPar; var parameterListEnd = methodDeclaration?.RPar; using (var cookie = new PsiTransactionCookie(psiServices, DefaultAction.Rollback, "RemoveIdentifier")) using (new DisableCodeFormatter()) { using (WriteLockCookie.Create()) { ModificationUtil.DeleteChild(identifierNode); if (typeUsage != null) { nameRange = nameRange.Shift(-typeUsage.GetTextLength()); ModificationUtil.DeleteChild(typeUsage); // Also delete the parameter list, if there is one. If there was an existing method declaration, // with parameter list and body, we would have fixed it by simply replacing the name. Deleting // an existing parameter list allows rewriting the return type, method name, parameter list and // body if (parameterListStart != null && parameterListEnd != null) { ModificationUtil.DeleteChildRange(parameterListStart, parameterListEnd); } else if (parameterListStart != null) { ModificationUtil.DeleteChild(parameterListStart); } else if (parameterListEnd != null) { ModificationUtil.DeleteChild(parameterListEnd); } } } cookie.Commit(); } // 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.InsertText(nameRange.StartOffset, "void Foo(){}"); psiServices.Files.CommitAllDocuments(); methodDeclaration = TextControlToPsi.GetElement <IMethodDeclaration>(solution, textControl); if (methodDeclaration == null) { return; } var attributeList = methodDeclaration.FirstChild as IAttributeSectionList; using (var cookie = new PsiTransactionCookie(psiServices, DefaultAction.Rollback, "RemoveInsertedDeclaration")) using (new DisableCodeFormatter()) { using (WriteLockCookie.Create()) ModificationUtil.DeleteChild(methodDeclaration); cookie.Commit(); } var classDeclaration = TextControlToPsi.GetElement <IClassLikeDeclaration>(solution, textControl); Assertion.AssertNotNull(classDeclaration, "classDeclaration != null"); var factory = CSharpElementFactory.GetInstance(classDeclaration); // Get the UnityEventFunction generator to actually insert the methods GenerateCodeWorkflowBase.ExecuteNonInteractive( GeneratorUnityKinds.UnityEventFunctions, solution, textControl, identifierNode.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 declaredElement = myEventFunction.CreateDeclaration(factory, classDeclaration, myAccessRights) .DeclaredElement.NotNull("declaredElement != null"); context.InputElements.Clear(); context.InputElements.Add(new GeneratorDeclaredElement(declaredElement)); }, onCompleted: context => { if (attributeList == null) { return; } methodDeclaration = TextControlToPsi.GetElement <IMethodDeclaration>(solution, textControl); if (methodDeclaration == null) { return; } // The Generate workflow adds a helper function to the queue to select the contents of the method. // Unfortunately, the offsets are calculated before adding the callback to the queue. If we modify // the PSI directly here, the offsets are incorrect and the selection is wrong. Doing it this way // loses the selection, but at least everything works. // Technically, we should probably add the attributes during the generation method, but then we'd // lose how multiple attributes are split into sections, etc. myShellLocks.Queue(Lifetime.Eternal, "FinishDeclaration", () => { using (ReadLockCookie.Create()) using (var transactionCookie = new PsiTransactionCookie(psiServices, DefaultAction.Rollback, "FinishDeclaration")) { methodDeclaration.SetAttributeSectionList(attributeList); transactionCookie.Commit(); } }); }); }