Ejemplo n.º 1
0
        /// <summary>
        /// create caret preserving edit transaction with automatic code change undo merging policy
        /// </summary>
        public static CaretPreservingEditTransaction CreateEditTransaction(
            this ITextView view, string description, ITextUndoHistoryRegistry registry, IEditorOperationsFactoryService service)
        {
            var transaction = new CaretPreservingEditTransaction(description, view, registry, service);
            transaction.MergePolicy = AutomaticCodeChangeMergePolicy.Instance;

            return transaction;
        }
Ejemplo n.º 2
0
        private void Commit(
            PresentationItem item, Model model, char? commitChar,
            ITextSnapshot initialTextSnapshot, Action nextHandler)
        {
            AssertIsForeground();

            // We could only be called if we had a model at this point.
            Contract.ThrowIfNull(model);

            // Now that we've captured the model at this point, we can stop ourselves entirely.  
            // This is also desirable as we may have reentrancy problems when we call into 
            // custom commit completion providers.  I.e. if the custom provider moves the caret,
            // then we do not want to process that move as it may put us into an unexpected state.
            //
            // TODO(cyrusn): We still have a general reentrancy problem where calling into a custom
            // commit provider (or just calling into the editor) may cause something to call back
            // into us.  However, for now, we just hope that no such craziness will occur.
            this.StopModelComputation();

            CompletionChange completionChange;
            using (var transaction = new CaretPreservingEditTransaction(
                EditorFeaturesResources.IntelliSense, TextView, _undoHistoryRegistry, _editorOperationsFactoryService))
            {
                // We want to merge with any of our other programmatic edits (e.g. automatic brace completion)
                transaction.MergePolicy = AutomaticCodeChangeMergePolicy.Instance;

                var provider = GetCompletionProvider(item.Item) as ICustomCommitCompletionProvider;
                if (provider != null)
                {
                    provider.Commit(item.Item, this.TextView, this.SubjectBuffer, model.TriggerSnapshot, commitChar);
                }
                else
                {
                    // Right before calling Commit, we may have passed the commitChar through to the
                    // editor.  That was so that undoing completion will get us back to the state we
                    // we would be in if completion had done nothing.  However, now that we're going
                    // to actually commit, we want to roll back to where we were before we pushed
                    // commit character into the buffer.  This has multiple benefits:
                    //
                    //   1) the buffer is in a state we expect it to be in.  i.e. we don't have to
                    //      worry about what might have happened (like brace-completion) when the
                    //      commit char was inserted.
                    //   2) after we commit the item, we'll pass the commit character again into the
                    //      buffer (unless the items asks us not to).  By doing this, we can make sure
                    //      that things like brace-completion or formatting trigger as we expect them
                    //      to.
                    var characterWasSentIntoBuffer = commitChar != null &&
                                                     initialTextSnapshot.Version.VersionNumber != this.SubjectBuffer.CurrentSnapshot.Version.VersionNumber;
                    if (characterWasSentIntoBuffer)
                    {
                        RollbackToBeforeTypeChar(initialTextSnapshot);
                    }

                    // Now, get the change the item wants to make.  Note that the change will be relative
                    // to the initial snapshot/document the item was triggered from.  We'll map that change
                    // forward, then apply it to our current snapshot.
                    var triggerDocument = model.TriggerDocument;
                    var triggerSnapshot = model.TriggerSnapshot;

                    var completionService = CompletionService.GetService(triggerDocument);
                    Contract.ThrowIfNull(completionService, nameof(completionService));

                    completionChange = completionService.GetChangeAsync(
                        triggerDocument, item.Item, commitChar, CancellationToken.None).WaitAndGetResult(CancellationToken.None);
                    var textChange = completionChange.TextChange;

                    var triggerSnapshotSpan = new SnapshotSpan(triggerSnapshot, textChange.Span.ToSpan());
                    var mappedSpan = triggerSnapshotSpan.TranslateTo(
                        this.SubjectBuffer.CurrentSnapshot, SpanTrackingMode.EdgeInclusive);

                    // Now actually make the text change to the document.
                    using (var textEdit = this.SubjectBuffer.CreateEdit(EditOptions.None, reiteratedVersionNumber: null, editTag: null))
                    {
                        var adjustedNewText = AdjustForVirtualSpace(textChange);

                        textEdit.Replace(mappedSpan.Span, adjustedNewText);
                        textEdit.Apply();
                    }

                    // adjust the caret position if requested by completion service
                    if (completionChange.NewPosition != null)
                    {
                        TextView.Caret.MoveTo(new SnapshotPoint(
                            this.SubjectBuffer.CurrentSnapshot, completionChange.NewPosition.Value));
                    }

                    // Now, pass along the commit character unless the completion item said not to
                    if (characterWasSentIntoBuffer && !completionChange.IncludesCommitCharacter)
                    {
                        nextHandler();
                    }

                    // If the insertion is long enough, the caret will scroll out of the visible area.
                    // Re-center the view.
                    this.TextView.Caret.EnsureVisible();
                }

                transaction.Complete();
            }

            // Let the completion rules know that this item was committed.
            this.MakeMostRecentItem(item.Item.DisplayText);
        }