internal TextContentChangedEventArgs ApplyReload(StringRebuilder newContent, EditOptions editOptions, object editTag) { // we construct a normalized change list where the inserted text is a reference string that // points "forward" to the next snapshot and whose deleted text is a reference string that points // "backward" to the prior snapshot. This pins both snapshots in memory but that's better than materializing // giant strings, and when (?) we have paging text storage, memory requirements will be minimal. TextVersion newVersion = new TextVersion(this, this.currentVersion.VersionNumber + 1, this.currentVersion.VersionNumber + 1, newContent.Length); ITextSnapshot oldSnapshot = this.currentSnapshot; TextSnapshot newSnapshot = new TextSnapshot(this, newVersion, newContent); ReferenceChangeString oldText = new ReferenceChangeString(new SnapshotSpan(oldSnapshot, 0, oldSnapshot.Length)); ReferenceChangeString newText = new ReferenceChangeString(new SnapshotSpan(newSnapshot, 0, newSnapshot.Length)); TextChange change = new TextChange(oldPosition: 0, oldText: oldText, newText: newText, currentSnapshot: oldSnapshot); this.currentVersion.AddNextVersion(NormalizedTextChangeCollection.Create(new FrugalList <TextChange>() { change }, editOptions.ComputeMinimalChange ? (StringDifferenceOptions?)editOptions.DifferenceOptions : null, this.textDifferencingService, oldSnapshot, newSnapshot), newVersion); this.builder = newContent; this.currentVersion = newVersion; this.currentSnapshot = newSnapshot; return(new TextContentChangedEventArgs(oldSnapshot, newSnapshot, editOptions, editTag)); }
public ReloadEdit(TextBuffer textBuffer, ITextSnapshot originSnapshot, EditOptions editOptions, object editTag) : base(textBuffer) { this.textBuffer = textBuffer; this.originSnapshot = originSnapshot; this.editOptions = editOptions; this.editTag = editTag; }
/// <summary> /// Replace the contents of the buffer with the contents of a different string rebuilder. /// </summary> /// <param name="newContent">The new contents of the buffer (presumably read from a file).</param> /// <param name="editOptions">Options to apply to the edit. Differencing is highly likely to be selected.</param> /// <param name="editTag">Arbitrary tag associated with the reload that will appear in event arguments.</param> /// <returns></returns> public ITextSnapshot ReloadContent(StringRebuilder newContent, EditOptions editOptions, object editTag) { using (ReloadEdit edit = new ReloadEdit(this, this.currentSnapshot, editOptions, editTag)) { return(edit.ReloadContent(newContent)); } }
public override BaseBuffer.ITextEventRaiser PropagateSourceChanges(EditOptions options, object editTag) { TextContentChangedEventRaiser raiser = IncorporateChanges(); raiser.RaiseEvent(this, true); return(raiser); }
internal TextContentChangedEventArgs ApplyReload(StringRebuilder newContent, EditOptions editOptions, object editTag) { // we construct a normalized change list where the inserted text is a reference string that // points "forward" to the next snapshot and whose deleted text is a reference string that points // "backward" to the prior snapshot. This pins both snapshots in memory but that's better than materializing // giant strings, and when (?) we have paging text storage, memory requirements will be minimal. ITextSnapshot oldSnapshot = this.currentSnapshot; StringRebuilder oldContent = BufferFactoryService.StringRebuilderFromSnapshotSpan(new SnapshotSpan(oldSnapshot, 0, oldSnapshot.Length)); TextChange change = TextChange.Create(oldPosition: 0, oldText: oldContent, newText: newContent, currentSnapshot: oldSnapshot); TextVersion newVersion = this.currentVersion.CreateNext(changes: null, newLength: newContent.Length, reiteratedVersionNumber: -1); TextSnapshot newSnapshot = new TextSnapshot(this, newVersion, newContent); this.currentVersion.SetChanges(NormalizedTextChangeCollection.Create(new TextChange[] { change }, editOptions.ComputeMinimalChange ? (StringDifferenceOptions?)editOptions.DifferenceOptions : null, this.textDifferencingService, oldSnapshot, newSnapshot)); this.currentVersion = newVersion; this.builder = newContent; this.currentSnapshot = newSnapshot; return(new TextContentChangedEventArgs(oldSnapshot, newSnapshot, editOptions, editTag)); }
private static void UpdateText( ImmutableArray <TextChange> textChanges, ITextBuffer buffer, ITextSnapshot oldSnapshot, SourceText oldText, EditOptions options ) { using var edit = buffer.CreateEdit( options, reiteratedVersionNumber: null, editTag: null ); if (CodeAnalysis.Workspace.TryGetWorkspace(oldText.Container, out var workspace)) { var undoService = workspace.Services.GetRequiredService <ISourceTextUndoService>(); undoService.BeginUndoTransaction(oldSnapshot); } foreach (var change in textChanges) { edit.Replace(change.Span.Start, change.Span.Length, change.NewText); } edit.ApplyAndLogExceptions(); }
public TextContentChangedEventRaiser(ITextSnapshot beforeSnapshot, ITextSnapshot afterSnapshot, EditOptions options, Object editTag) { args = new TextContentChangedEventArgs(beforeSnapshot, afterSnapshot, options, editTag); }
internal static void UpdateText(SourceText newText, ITextBuffer buffer, EditOptions options) { var oldSnapshot = buffer.CurrentSnapshot; var oldText = oldSnapshot.AsText(); var changes = newText.GetTextChanges(oldText); UpdateText(changes.ToImmutableArray(), buffer, oldSnapshot, oldText, options); }
public EditOptions(int tab, string d, object o, BindingSource bs, EditOptions detail, TabPage custom, bool camptable) : this(tab, d, o, bs, detail) { tabControl = custom; //Fixup leftisms; //if (gBox != null) { gBox.Left = TableLocation.X; gBox.Top = TableLocation.Y; } isCampignTable = camptable; }
public EditOptions(int tab, string d, object o, BindingSource bs, EditOptions detail) { tabType = tab; Description = d; EmptyType = o; SetBinding(bs); Detail = detail; }
public ITextEdit CreateEdit(EditOptions options, int?reiteratedVersionNumber, object editTag) { VerifyAccess(); if (EditInProgress) { throw new InvalidOperationException("An edit operation is in progress"); } return(textEditInProgress = new TextEdit(this, options, reiteratedVersionNumber, editTag)); }
private void PrepareEdit(out int bookISBN, out EditOptions editOptions) { int.TryParse(mainMenuView.GetUserInput("Type book ISBN: "), out bookISBN); editBookView.PrintMenu(); int sthToEdit; int.TryParse(mainMenuView.GetUserInput("What do you want to edit? "), out sthToEdit); editOptions = (EditOptions)sthToEdit; }
public TextEdit(TextBuffer textBuffer, EditOptions options, int?reiteratedVersionNumber, object editTag) { this.textBuffer = textBuffer ?? throw new ArgumentNullException(nameof(textBuffer)); TextSnapshot = textBuffer.CurrentSnapshot; changes = new List <ITextChange>(); this.options = options; this.reiteratedVersionNumber = reiteratedVersionNumber; this.editTag = editTag; }
public TextEdit(TextBuffer textBuffer, EditOptions options, int? reiteratedVersionNumber, object editTag) { if (textBuffer == null) throw new ArgumentNullException(nameof(textBuffer)); this.textBuffer = textBuffer; TextSnapshot = textBuffer.CurrentSnapshot; changes = new List<ITextChange>(); this.options = options; this.reiteratedVersionNumber = reiteratedVersionNumber; this.editTag = editTag; }
protected Edit(BaseBuffer baseBuffer, ITextSnapshot originSnapshot, EditOptions options, int?reiteratedVersionNumber, Object editTag) : base(baseBuffer, originSnapshot, editTag) { this.bufferLength = originSnapshot.Length; this.changes = new FrugalList <TextChange>(); this.options = options; this.reiteratedVersionNumber = reiteratedVersionNumber; this.raisedChangingEventArgs = null; this.cancelAction = null; this.hasFailedChanges = false; }
public static void UpdateText( ImmutableArray <TextChange> textChanges, ITextBuffer buffer, EditOptions options ) { var oldSnapshot = buffer.CurrentSnapshot; var oldText = oldSnapshot.AsText(); UpdateText(textChanges, buffer, oldSnapshot, oldText, options); }
// Stolen from Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.DocumentProvider.StandardTextDocument private static void UpdateText(SourceText newText, ITextBuffer buffer, EditOptions options) { using (ITextEdit textEdit = buffer.CreateEdit(options, null, null)) { SourceText oldText = buffer.CurrentSnapshot.AsText(); foreach (TextChange current in newText.GetTextChanges(oldText)) { textEdit.Replace(current.Span.Start, current.Span.Length, current.NewText); } textEdit.Apply(); } }
public EditTaskResults(Task task, ObservableCollection <ResultInfo> results, EditOptions editOptions) : base(results, editOptions, true) { InitializeComponent(); #if DEBUG buttonRandom.Visibility = System.Windows.Visibility.Visible; #endif this.task = task; }
public ITextEdit GetEdit(BaseBuffer buffer, EditOptions options) { ISubordinateTextEdit subedit; if (!this.buffer2EditMap.TryGetValue(buffer, out subedit)) { Debug.Assert(!(buffer is IProjectionBufferBase)); subedit = buffer.CreateSubordinateEdit(options, null, this.masterEditTag); this.buffer2EditMap.Add(buffer, subedit); } return((ITextEdit)subedit); }
private static void UpdateText(SourceText newText, ITextBuffer buffer, EditOptions options) { using (var edit = buffer.CreateEdit(options, reiteratedVersionNumber: null, editTag: null)) { var oldText = buffer.CurrentSnapshot.AsText(); var changes = newText.GetTextChanges(oldText); foreach (var change in changes) { edit.Replace(change.Span.Start, change.Span.Length, change.NewText); } edit.Apply(); } }
internal void ApplyChanges(TextEdit textEdit, List <ITextChange> changes, EditOptions options, int?reiteratedVersionNumber, object editTag) { VerifyAccess(); if (textEdit != textEditInProgress) { throw new InvalidOperationException(); } textEditInProgress = null; if (RaiseChangingGetIsCanceled(editTag)) { PostChanged?.Invoke(this, EventArgs.Empty); return; } if (changes.Count != 0) { // We don't support overlapping changes. All offsets are relative to the original buffer changes.Sort((a, b) => b.OldPosition - a.OldPosition); for (int i = 1; i < changes.Count; i++) { if (changes[i - 1].OldSpan.OverlapsWith(changes[i].OldSpan)) { throw new InvalidOperationException("Two edit operations overlap"); } } var beforeSnapshot = CurrentSnapshot; Document.BeginUpdate(); // changes is sorted in reverse order by OldPosition foreach (var change in changes) { Document.Replace(change.OldPosition, change.OldLength, change.NewText); } Document.EndUpdate(); CreateNewCurrentSnapshot(changes, afterTextSource: Document.CreateSnapshot()); var afterSnapshot = CurrentSnapshot; TextContentChangedEventArgs args = null; //TODO: The event handlers are allowed to modify the buffer, but the new events must only be // raised after all of these three events have been raised. ChangedHighPriority?.Invoke(this, args ?? (args = new TextContentChangedEventArgs(beforeSnapshot, afterSnapshot, options, editTag))); Changed?.Invoke(this, args ?? (args = new TextContentChangedEventArgs(beforeSnapshot, afterSnapshot, options, editTag))); ChangedLowPriority?.Invoke(this, args ?? (args = new TextContentChangedEventArgs(beforeSnapshot, afterSnapshot, options, editTag))); } //TODO: Use reiteratedVersionNumber PostChanged?.Invoke(this, EventArgs.Empty); }
private void ReloadBufferFromStream(Stream stream, long fileSize, EditOptions options, Encoding encoding) { using (var streamReader = new EncodedStreamReader.NonStreamClosingStreamReader(stream, encoding, detectEncodingFromByteOrderMarks: false)) { TextBuffer concreteBuffer = _textBuffer as TextBuffer; if (concreteBuffer != null) { StringRebuilder newContent = TextImageLoader.Load( streamReader, fileSize, out var newlineState, out var leadingWhitespaceState, out var longestLineLength); if (!newlineState.HasConsistentLineEndings) { // leave a sign that line endings are inconsistent. This is rather nasty but for now // we don't want to pollute the API with this factoid. concreteBuffer.Properties["InconsistentLineEndings"] = true; } else { // this covers a really obscure case where on initial load the file had inconsistent line // endings, but the UI settings were such that it was ignored, and since then the file has // acquired consistent line endings and the UI settings have also changed. concreteBuffer.Properties.RemoveProperty("InconsistentLineEndings"); } // leave a similar sign about the longest line in the buffer. concreteBuffer.Properties["LongestLineLength"] = longestLineLength; concreteBuffer.ReloadContent(newContent, options, editTag: this); } else { // we may hit this path if somebody mocks the text buffer in a test. using (var edit = _textBuffer.CreateEdit(options, null, editTag: this)) { if (edit.Replace(new Span(0, edit.Snapshot.Length), streamReader.ReadToEnd())) { edit.Apply(); } else { edit.Cancel(); } } } } }
public EditCollection(ObservableCollection <T> collection, EditOptions editOptions, bool buffered = false) { DataContext = this; options = editOptions; this.buffered = buffered; if (buffered) { SaveCollection = collection; DataGridCollection = new ObservableCollection <T>(); collection.CopyTo(DataGridCollection); } else { DataGridCollection = collection; } }
protected void LoadEditor() { if (lboxSystemTables.SelectedIndex == -1) { return; } //Hide current editor cEditor = lboxSystemTables.Items[lboxSystemTables.SelectedIndex] as EditOptions; switch (cEditor.tabType) { case isTable: SetMessage("Set Table"); PaintTable(); break; case isTree: SetMessage("Set tree"); PaintTree(); break; case isMasterDetail: SetMessage("Set MasterDetail"); PaintMasterDetail(); break; default: SetMessage("TAB Type Error for" + cEditor.Description); break; } switch (cEditor.Description) { case strAlign: SetMessage("Load Alignments"); break; case strDevType: SetMessage("Load DevTypes"); break; } }
internal static void UpdateText(SourceText newText, ITextBuffer buffer, EditOptions options) { using var edit = buffer.CreateEdit(options, reiteratedVersionNumber: null, editTag: null); var oldSnapshot = buffer.CurrentSnapshot; var oldText = oldSnapshot.AsText(); var changes = newText.GetTextChanges(oldText); if (CodeAnalysis.Workspace.TryGetWorkspace(oldText.Container, out var workspace)) { var undoService = workspace.Services.GetService <ISourceTextUndoService>(); undoService.BeginUndoTransaction(oldSnapshot); } foreach (var change in changes) { edit.Replace(change.Span.Start, change.Span.Length, change.NewText); } edit.ApplyAndLogExceptions(); }
/// <summary> /// Initializes a new instance of a <see cref="ProjectionSourceSpansChangedEventArgs"/>. /// </summary> /// <param name="beforeSnapshot">The most recent <see cref="IProjectionSnapshot"/> before the change occurred.</param> /// <param name="afterSnapshot">The <see cref="IProjectionSnapshot"/> immediately after the change occurred.</param> /// <param name="insertedSpans">Zero or more source spans that were inserted into the <see cref="IProjectionBuffer"/>.</param> /// <param name="deletedSpans">Zero or more source spans that were deleted from the <see cref="IProjectionBuffer"/>.</param> /// <param name="spanPosition">The position at which the span changes occurred.</param> /// <param name="options">The edit options that were applied to this change.</param> /// <param name="editTag">An arbitrary object associated with this change.</param> /// <exception cref="ArgumentNullException">One of the parameters: <paramref name="beforeSnapshot"/>, <paramref name="afterSnapshot"/>, /// <paramref name="insertedSpans"/>, or <paramref name="deletedSpans"/>is null.</exception> public ProjectionSourceSpansChangedEventArgs(IProjectionSnapshot beforeSnapshot, IProjectionSnapshot afterSnapshot, IList <ITrackingSpan> insertedSpans, IList <ITrackingSpan> deletedSpans, int spanPosition, EditOptions options, object editTag) : base(beforeSnapshot, afterSnapshot, options, editTag) { if (insertedSpans == null) { throw new ArgumentNullException("insertedSpans"); } if (deletedSpans == null) { throw new ArgumentNullException("deletedSpans"); } this.insertedSpans = new ReadOnlyCollection <ITrackingSpan>(insertedSpans); this.deletedSpans = new ReadOnlyCollection <ITrackingSpan>(deletedSpans); this.spanPosition = spanPosition; }
/// <summary> /// Initializes a new instance of a <see cref="ProjectionSourceBuffersChangedEventArgs"/> object. /// </summary> /// <param name="beforeSnapshot">The most recent <see cref="IProjectionSnapshot"/> before the change occurred.</param> /// <param name="afterSnapshot">The <see cref="IProjectionSnapshot"/> immediately after the change occurred.</param> /// <param name="insertedSpans">Zero or more source spans that were inserted into the <see cref="IProjectionBuffer"/>.</param> /// <param name="deletedSpans">Zero or more source spans that were deleted from the <see cref="IProjectionBuffer"/>.</param> /// <param name="spanPosition">The position in the list of source spans at which the buffer changes occurred.</param> /// <param name="addedBuffers">The list of added source <see cref="ITextBuffer"/> objects.</param> /// <param name="removedBuffers">The list of removed source <see cref="ITextBuffer"/> objects.</param> /// <param name="options">The edit options that were applied to this change.</param> /// <param name="editTag">An arbitrary object associated with this change.</param> /// <exception cref="ArgumentNullException"><paramref name="insertedSpans"/> is null.</exception> /// <exception cref="ArgumentNullException"><paramref name="deletedSpans"/> is null.</exception> /// <exception cref="ArgumentNullException"><paramref name="addedBuffers"/> or <paramref name="removedBuffers"/> is null.</exception> public ProjectionSourceBuffersChangedEventArgs(IProjectionSnapshot beforeSnapshot, IProjectionSnapshot afterSnapshot, IList <ITrackingSpan> insertedSpans, IList <ITrackingSpan> deletedSpans, int spanPosition, IList <ITextBuffer> addedBuffers, IList <ITextBuffer> removedBuffers, EditOptions options, object editTag) : base(beforeSnapshot, afterSnapshot, insertedSpans, deletedSpans, spanPosition, options, editTag) { if (addedBuffers == null) { throw new ArgumentNullException("addedBuffers"); } if (removedBuffers == null) { throw new ArgumentNullException("removedBuffers"); } this.addedBuffers = addedBuffers; this.removedBuffers = removedBuffers; }
private static int HandleEdit(IJournalrService service, EditOptions opts) { var entry = service.GetEntry(opts.Id); if (opts.Body != null) { entry.Text = opts.Body; } if (opts.DateTime != null) { entry.EntryDate = new Chronic.Parser().Parse(opts.DateTime).Start.Value; } if (opts.Tags != null) { var newTags = new List <string>(); foreach (var tag in opts.Tags.Split(',')) { if (!entry.Tags.Contains(tag)) { newTags.Add(tag); } } entry.Tags = newTags; } if (service.UpdateEntry(entry)) { Console.WriteLine("Edit successful"); } else { Console.WriteLine("Edit failed"); } return(0); }
/// <summary> /// Provides metadata to the model metadata creation process. /// </summary> /// <param name="metadata">The model metadata.</param> protected override void InternalOnMetadataCreated(ModelMetadata metadata) { EditOptions.DataEvents = DataEvents; EditOptions.DataUrl = DataUrl; EditOptions.HtmlAttributes = HtmlAttributes; EditOptions.PostData = PostData; EditOptions.ValueDictionary = ValueDictionary; if (EditType == JqGridColumnEditTypes.JQueryUIAutocomplete) { EditType = JqGridColumnEditTypes.Text; EditOptions.ConfigureJQueryUIAutocomplete(); } else if (EditType == JqGridColumnEditTypes.JQueryUIDatepicker) { EditType = JqGridColumnEditTypes.Text; EditOptions.ConfigureJQueryUIDatepicker(metadata); } else if (EditType == JqGridColumnEditTypes.JQueryUISpinner) { EditType = JqGridColumnEditTypes.Text; EditOptions.ConfigureJQueryUISpinner(); } else if (EditType == JqGridColumnEditTypes.SelectsCascadeParent) { EditType = JqGridColumnEditTypes.Select; EditOptions.ConfigureSelectsCascadeParent(); } metadata.SetColumnDateFormat(DateFormat); metadata.SetColumnEditable(Editable); metadata.SetColumnEditOptions(EditOptions); metadata.SetColumnEditRules(Rules); metadata.SetColumnEditType(EditType); metadata.SetColumnFormOptions(FormOptions); }
private void ReloadBufferFromStream(Stream stream, long fileSize, EditOptions options, Encoding encoding) { using (var streamReader = new EncodedStreamReader.NonStreamClosingStreamReader(stream, encoding, detectEncodingFromByteOrderMarks: false)) { TextBuffer concreteBuffer = _textBuffer as TextBuffer; if (concreteBuffer != null) { int longestLineLength; StringRebuilder newContent = TextImageLoader.Load(streamReader, fileSize, out var newlineState, out var leadingWhitespaceState, out longestLineLength); // Make the call to GetWhitespaceManager to add the manager to the properties. We don't need the return value here. _textDocumentFactoryService.WhitespaceManagerFactory.GetOrCreateWhitespaceManager(concreteBuffer, newlineState, leadingWhitespaceState); // Leave a sign about the longest line in the buffer. This is rather nasty, but for now // we don't want to pollute the API with this factoid concreteBuffer.Properties["LongestLineLength"] = longestLineLength; concreteBuffer.ReloadContent(newContent, options, editTag: this); } else { // we may hit this path if somebody mocks the text buffer in a test. using (var edit = _textBuffer.CreateEdit(options, null, editTag: this)) { if (edit.Replace(new Span(0, edit.Snapshot.Length), streamReader.ReadToEnd())) { edit.Apply(); } else { edit.Cancel(); } } } } }
public ReloadResult Reload(EditOptions options) => ReloadResult.Succeeded;
public ReloadResult Reload(EditOptions options) { throw new NotImplementedException(); }
private static void UpdateText(SourceText newText, ITextBuffer buffer, EditOptions options) { using (var edit = buffer.CreateEdit(options, reiteratedVersionNumber: null, editTag: null)) { var oldSnapshot = buffer.CurrentSnapshot; var oldText = oldSnapshot.AsText(); var changes = newText.GetTextChanges(oldText); Workspace workspace = null; if (Workspace.TryGetWorkspace(oldText.Container, out workspace)) { var undoService = workspace.Services.GetService<ISourceTextUndoService>(); undoService.BeginUndoTransaction(oldSnapshot); } foreach (var change in changes) { edit.Replace(change.Span.Start, change.Span.Length, change.NewText); } edit.Apply(); } }
public ITextEdit CreateEdit(EditOptions options, int? reiteratedVersionNumber, object editTag) { return new TestEdit(this.snapshot); }
public ITextEdit CreateEdit(EditOptions options, int? reiteratedVersionNumber, object editTag) { return new TextEditMock(this); }
public ITextEdit CreateEdit(EditOptions options, int? reiteratedVersionNumber, object editTag) { VerifyAccess(); if (EditInProgress) throw new InvalidOperationException("An edit operation is in progress"); return textEditInProgress = new TextEdit(this, options, reiteratedVersionNumber, editTag); }
// Stolen from Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.DocumentProvider.StandardTextDocument private static void UpdateText(SourceText newText, ITextBuffer buffer, EditOptions options) { using (ITextEdit textEdit = buffer.CreateEdit(options, null, null)) { SourceText oldText = buffer.CurrentSnapshot.AsText(); foreach (var current in newText.GetTextChanges(oldText)) { textEdit.Replace(current.Span.Start, current.Span.Length, current.NewText); } textEdit.Apply(); } }
private void Commit(PresentationItem item, Model model, char? commitChar, CancellationToken cancellationToken) { var textChanges = ImmutableArray<TextChange>.Empty; // NOTE(cyrusn): It is intentional that we get the undo history for the // surface buffer and not the subject buffer. // There have been some watsons where the ViewBuffer hadn't been registered, // so use TryGetHistory instead. ITextUndoHistory undoHistory; _undoHistoryRegistry.TryGetHistory(this.TextView.TextBuffer, out undoHistory); using (var transaction = undoHistory?.CreateTransaction(EditorFeaturesResources.IntelliSense)) { // We want to merge with any of our other programmatic edits (e.g. automatic brace completion) if (transaction != null) { transaction.MergePolicy = AutomaticCodeChangeMergePolicy.Instance; } // Check if the provider wants to perform custom commit itself. Otherwise we will // handle things. var provider = GetCompletionProvider(item.Item) as ICustomCommitCompletionProvider; if (provider == null) { var viewBuffer = this.TextView.TextBuffer; var commitDocument = this.SubjectBuffer.CurrentSnapshot.AsText().GetDocumentWithFrozenPartialSemanticsAsync(cancellationToken).WaitAndGetResult(cancellationToken); // adjust commit item span foward to match current document that is passed to GetChangeAsync below var commitItem = item.Item; var currentItemSpan = GetCurrentItemSpan(commitItem, model); commitItem = commitItem.WithSpan(currentItemSpan); var completionService = CompletionService.GetService(commitDocument); var commitChange = completionService.GetChangeAsync(commitDocument, commitItem, commitChar, cancellationToken).WaitAndGetResult(cancellationToken); textChanges = commitChange.TextChanges; // Use character based diffing here to avoid overwriting the commit character placed into the editor. var editOptions = new EditOptions(new StringDifferenceOptions { DifferenceType = StringDifferenceTypes.Character, IgnoreTrimWhiteSpace = EditOptions.DefaultMinimalChange.DifferenceOptions.IgnoreTrimWhiteSpace }); // edit subject buffer (not view) because text changes are in terms of current document. using (var textEdit = this.SubjectBuffer.CreateEdit(editOptions, reiteratedVersionNumber: null, editTag: null)) { for (int iChange = 0; iChange < textChanges.Length; iChange++) { var textChange = textChanges[iChange]; var isFirst = iChange == 0; var isLast = iChange == textChanges.Length - 1; // add commit char to end of last change if not already included if (isLast && !commitChange.IncludesCommitCharacter && commitChar.HasValue) { textChange = new TextChange(textChange.Span, textChange.NewText + commitChar.Value); } var currentSpan = new SnapshotSpan(this.SubjectBuffer.CurrentSnapshot, new Span(textChange.Span.Start, textChange.Span.Length)); // In order to play nicely with automatic brace completion, we need to // not touch the opening paren. We'll check our span and textchange // for ( and adjust them accordingly if we find them. // all this is needed since we don't use completion set mechanism provided by VS but we implement everything ourselves. // due to that, existing brace completion engine in editor that should take care of interaction between brace completion // and intellisense doesn't work for us. so we need this kind of workaround to support it nicely. bool textChanged; string newText = textChange.NewText; if (isFirst) { newText = AdjustFirstText(textChange); } if (isLast) { newText = AdjustLastText(newText, commitChar.GetValueOrDefault(), out textChanged); currentSpan = AdjustLastSpan(currentSpan, commitChar.GetValueOrDefault(), textChanged); } var caretPoint = this.TextView.GetCaretPoint(this.SubjectBuffer); var virtualCaretPoint = this.TextView.GetVirtualCaretPoint(this.SubjectBuffer); if (caretPoint.HasValue && virtualCaretPoint.HasValue) { // TODO(dustinca): We need to call a different API here. TryMoveCaretToAndEnsureVisible might center within the view. this.TextView.TryMoveCaretToAndEnsureVisible(new VirtualSnapshotPoint(caretPoint.Value)); } caretPoint = this.TextView.GetCaretPoint(this.SubjectBuffer); // Now that we're doing character level diffing, we need to move the caret to the end of // the span being replaced. Otherwise, we can replace M|ai with Main and wind up with // M|ain, since character based diffing makes that quite legit. if (caretPoint.HasValue) { var endInSubjectBuffer = this.TextView.BufferGraph.MapDownToBuffer(currentSpan.End, PointTrackingMode.Positive, caretPoint.Value.Snapshot.TextBuffer, PositionAffinity.Predecessor); if (caretPoint.Value < endInSubjectBuffer) { this.TextView.TryMoveCaretToAndEnsureVisible(new SnapshotPoint(currentSpan.Snapshot.TextBuffer.CurrentSnapshot, currentSpan.End.Position)); } } textEdit.Replace(currentSpan, newText); } textEdit.Apply(); } // adjust the caret position if requested by completion service if (commitChange.NewPosition != null) { var target = new SnapshotPoint(this.SubjectBuffer.CurrentSnapshot, commitChange.NewPosition.Value); this.TextView.TryMoveCaretToAndEnsureVisible(target); } // We've manipulated the caret position in order to generate the correct edit. However, // if the insertion is long enough, the caret will scroll out of the visible area. // Re-center the view. using (var textEdit = viewBuffer.CreateEdit(editOptions, reiteratedVersionNumber: null, editTag: null)) { var caretPoint = this.TextView.GetCaretPoint(this.SubjectBuffer); if (caretPoint.HasValue) { this.TextView.Caret.EnsureVisible(); } } transaction?.Complete(); } else { // Let the provider handle this. provider.Commit(item.Item, this.TextView, this.SubjectBuffer, model.TriggerSnapshot, commitChar); transaction?.Complete(); } } var document = this.SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); var formattingService = document.GetLanguageService<IEditorFormattingService>(); var commitCharTriggersFormatting = commitChar != null && (formattingService?.SupportsFormattingOnTypedCharacter(document, commitChar.GetValueOrDefault()) ?? false); if (formattingService != null && (item.Item.Rules.FormatOnCommit || commitCharTriggersFormatting)) { // Formatting the completion item affected span is done as a separate transaction because this gives the user // the flexibility to undo the formatting but retain the changes associated with the completion item using (var formattingTransaction = _undoHistoryRegistry.GetHistory(this.TextView.TextBuffer).CreateTransaction(EditorFeaturesResources.IntelliSenseCommitFormatting)) { var caretPoint = this.TextView.GetCaretPoint(this.SubjectBuffer); IList<TextChange> changes = null; if (commitCharTriggersFormatting && caretPoint.HasValue) { // if the commit character is supported by formatting service, then let the formatting service // find the appropriate range to format. changes = formattingService.GetFormattingChangesAsync(document, commitChar.Value, caretPoint.Value.Position, cancellationToken).WaitAndGetResult(cancellationToken); } else if (textChanges.Length > 0) { // if this is not a supported trigger character for formatting service (space or tab etc.) // then format the span of the textchange. var totalSpan = TextSpan.FromBounds(textChanges.Min(c => c.Span.Start), textChanges.Max(c => c.Span.End)); changes = formattingService.GetFormattingChangesAsync(document, totalSpan, cancellationToken).WaitAndGetResult(cancellationToken); } if (changes != null && !changes.IsEmpty()) { document.Project.Solution.Workspace.ApplyTextChanges(document.Id, changes, cancellationToken); } formattingTransaction.Complete(); } } // Let the completion rules know that this item was committed. this.MakeMostRecentItem(item.Item.DisplayText); }
private void Commit(CompletionItem item, TextChange textChange, Model model, char? commitChar) { AssertIsForeground(); // We could only be called if we had a model at this point. Contract.ThrowIfNull(model); item = Controller.GetExternallyUsableCompletionItem(item); // 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(); // NOTE(cyrusn): It is intentional that we get the undo history for the // surface buffer and not the subject buffer. using (var transaction = _undoHistoryRegistry.GetHistory(this.TextView.TextBuffer).CreateTransaction(EditorFeaturesResources.IntelliSense)) { // We want to merge with any of our other programmatic edits (e.g. automatic brace completion) transaction.MergePolicy = AutomaticCodeChangeMergePolicy.Instance; // Check if the provider wants to perform custom commit itself. Otherwise we will // handle things. var provider = item.CompletionProvider as ICustomCommitCompletionProvider; if (provider == null) { var viewBuffer = this.TextView.TextBuffer; // Use character based diffing here to avoid overwriting the commit character placed into the editor. var editOptions = new EditOptions(new StringDifferenceOptions { DifferenceType = StringDifferenceTypes.Character, IgnoreTrimWhiteSpace = EditOptions.DefaultMinimalChange.DifferenceOptions.IgnoreTrimWhiteSpace }); using (var textEdit = viewBuffer.CreateEdit(editOptions, reiteratedVersionNumber: null, editTag: null)) { var viewSpan = model.GetSubjectBufferFilterSpanInViewBuffer(textChange.Span); var currentSpan = model.GetCurrentSpanInSnapshot(viewSpan, viewBuffer.CurrentSnapshot); // In order to play nicely with automatic brace completion, we need to // not touch the opening paren. We'll check our span and textchange // for ( and adjust them accordingly if we find them. // all this is needed since we don't use completion set mechanism provided by VS but we implement everything ourselves. // due to that, existing brace completion engine in editor that should take care of interaction between brace completion // and intellisense doesn't work for us. so we need this kind of workaround to support it nicely. bool textChanged; var finalText = GetFinalText(textChange, commitChar.GetValueOrDefault(), out textChanged); currentSpan = GetFinalSpan(currentSpan, commitChar.GetValueOrDefault(), textChanged); var caretPoint = this.TextView.GetCaretPoint(this.SubjectBuffer); var virtualCaretPoint = this.TextView.GetVirtualCaretPoint(this.SubjectBuffer); if (caretPoint.HasValue && virtualCaretPoint.HasValue) { // TODO(dustinca): We need to call a different API here. TryMoveCaretToAndEnsureVisible might center within the view. this.TextView.TryMoveCaretToAndEnsureVisible(new VirtualSnapshotPoint(caretPoint.Value)); } caretPoint = this.TextView.GetCaretPoint(this.SubjectBuffer); // Now that we're doing character level diffing, we need to move the caret to the end of // the span being replaced. Otherwise, we can replace M|ai with Main and wind up with // M|ain, since character based diffing makes that quite legit. if (caretPoint.HasValue) { var endInSubjectBuffer = this.TextView.BufferGraph.MapDownToBuffer(currentSpan.End, PointTrackingMode.Positive, caretPoint.Value.Snapshot.TextBuffer, PositionAffinity.Predecessor); if (caretPoint.Value < endInSubjectBuffer) { this.TextView.TryMoveCaretToAndEnsureVisible(new SnapshotPoint(currentSpan.Snapshot.TextBuffer.CurrentSnapshot, currentSpan.End.Position)); } } textEdit.Replace(currentSpan, finalText); textEdit.Apply(); } // We've manipulated the caret position in order to generate the correct edit. However, // if the insertion is long enough, the caret will scroll out of the visible area. // Re-center the view. using (var textEdit = viewBuffer.CreateEdit(editOptions, reiteratedVersionNumber: null, editTag: null)) { var caretPoint = this.TextView.GetCaretPoint(this.SubjectBuffer); if (caretPoint.HasValue) { this.TextView.Caret.EnsureVisible(); } } transaction.Complete(); } else { // Let the provider handle this. provider.Commit(item, this.TextView, this.SubjectBuffer, model.TriggerSnapshot, commitChar); transaction.Complete(); } } var document = this.SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); var formattingService = document.GetLanguageService<IEditorFormattingService>(); var commitCharTriggersFormatting = commitChar != null && (formattingService?.SupportsFormattingOnTypedCharacter(document, commitChar.GetValueOrDefault()) ?? false); if (formattingService != null && (item.ShouldFormatOnCommit || commitCharTriggersFormatting)) { // Formatting the completion item affected span is done as a separate transaction because this gives the user // the flexibility to undo the formatting but retain the changes associated with the completion item using (var formattingTransaction = _undoHistoryRegistry.GetHistory(this.TextView.TextBuffer).CreateTransaction(EditorFeaturesResources.IntelliSenseCommitFormatting)) { var caretPoint = this.TextView.GetCaretPoint(this.SubjectBuffer); IList<TextChange> changes; if (commitCharTriggersFormatting && caretPoint.HasValue) { // if the commit character is supported by formatting service, then let the formatting service // find the appropriate range to format. changes = formattingService.GetFormattingChangesAsync(document, commitChar.Value, caretPoint.Value.Position, CancellationToken.None).WaitAndGetResult(CancellationToken.None); } else { // if this is not a supported trigger character for formatting service (space or tab etc.) // then format the span of the textchange. changes = formattingService.GetFormattingChangesAsync(document, textChange.Span, CancellationToken.None).WaitAndGetResult(CancellationToken.None); } if (changes != null && !changes.IsEmpty()) { document.Project.Solution.Workspace.ApplyTextChanges(document.Id, changes, CancellationToken.None); } formattingTransaction.Complete(); } } // Let the completion rules know that this item was committed. GetCompletionRules().CompletionItemCommitted(item); }
public ITextEdit CreateEdit(EditOptions options, int? reiteratedVersionNumber, object editTag) { throw new NotImplementedException(); }
public ITextEdit CreateEdit(EditOptions options, int?reiteratedVersionNumber, object editTag) { throw new NotImplementedException(); }
internal void ApplyChanges(TextEdit textEdit, List<ITextChange> changes, EditOptions options, int? reiteratedVersionNumber, object editTag) { VerifyAccess(); if (textEdit != textEditInProgress) throw new InvalidOperationException(); textEditInProgress = null; if (RaiseChangingGetIsCanceled(editTag)) { PostChanged?.Invoke(this, EventArgs.Empty); return; } if (changes.Count != 0) { // We don't support overlapping changes. All offsets are relative to the original buffer changes.Sort((a, b) => b.OldPosition - a.OldPosition); for (int i = 1; i < changes.Count; i++) { if (changes[i - 1].OldSpan.OverlapsWith(changes[i].OldSpan)) throw new InvalidOperationException("Two edit operations overlap"); } var beforeSnapshot = CurrentSnapshot; Document.BeginUpdate(); // changes is sorted in reverse order by OldPosition foreach (var change in changes) Document.Replace(change.OldPosition, change.OldLength, change.NewText); Document.EndUpdate(); CreateNewCurrentSnapshot(changes, reiteratedVersionNumber, Document.CreateSnapshot()); var afterSnapshot = CurrentSnapshot; TextContentChangedEventArgs args = null; //TODO: The event handlers are allowed to modify the buffer, but the new events must only be // raised after all of these three events have been raised. ChangedHighPriority?.Invoke(this, args ?? (args = new TextContentChangedEventArgs(beforeSnapshot, afterSnapshot, options, editTag))); Changed?.Invoke(this, args ?? (args = new TextContentChangedEventArgs(beforeSnapshot, afterSnapshot, options, editTag))); ChangedLowPriority?.Invoke(this, args ?? (args = new TextContentChangedEventArgs(beforeSnapshot, afterSnapshot, options, editTag))); } PostChanged?.Invoke(this, EventArgs.Empty); }