public void UpdateText(SourceText newText) { _updatding = true; _editor.Document.BeginUpdate(); try { var changes = newText.GetTextChanges(_currentText); var offset = 0; foreach (var change in changes) { _editor.Document.Replace(change.Span.Start + offset, change.Span.Length, new StringTextSource(change.NewText)); offset += change.NewText.Length - change.Span.Length; } _currentText = newText; } finally { _updatding = false; _editor.Document.EndUpdate(); } }
protected override void ApplyDocumentTextChanged(DocumentId document, SourceText newText) { if (_openDocumentId != document) { return; } ITextSnapshot appliedText; using (var edit = _openTextContainer.GetTextBuffer().CreateEdit(EditOptions.DefaultMinimalChange, reiteratedVersionNumber: null, editTag: null)) { var oldText = _openTextContainer.CurrentText; var changes = newText.GetTextChanges(oldText); foreach (var change in changes) { edit.Replace(change.Span.Start, change.Span.Length, change.NewText); } appliedText = edit.Apply(); } this.OnDocumentTextChanged(document, appliedText.AsText(), PreservationMode.PreserveIdentity); }
protected override async void ApplyDocumentTextChanged (DocumentId id, SourceText text) { var document = GetDocument (id); if (document == null) return; bool isOpen; var filePath = document.FilePath; Projection projection = null; foreach (var entry in ProjectionList) { var p = entry.Projections.FirstOrDefault (proj => proj?.Document?.FileName != null && FilePath.PathComparer.Equals (proj.Document.FileName, filePath)); if (p != null) { filePath = entry.File.FilePath; projection = p; break; } } var data = TextFileProvider.Instance.GetTextEditorData (filePath, out isOpen); // Guard against already done changes in linked files. // This shouldn't happen but the roslyn merging seems not to be working correctly in all cases :/ if (document.GetLinkedDocumentIds ().Length > 0 && isOpen && !(text.GetType ().FullName == "Microsoft.CodeAnalysis.Text.ChangedText")) { return; } SourceText formerText; lock (changedFiles) { if (changedFiles.TryGetValue (filePath, out formerText)) { if (formerText.Length == text.Length && formerText.ToString () == text.ToString ()) return; } changedFiles [filePath] = text; } SourceText oldFile; if (!isOpen || !document.TryGetText (out oldFile)) { oldFile = await document.GetTextAsync (); } var changes = text.GetTextChanges (oldFile).OrderByDescending (c => c.Span.Start).ToList (); int delta = 0; if (!isOpen) { delta = ApplyChanges (projection, data, changes); var formatter = CodeFormatterService.GetFormatter (data.MimeType); if (formatter.SupportsPartialDocumentFormatting) { var mp = GetMonoProject (CurrentSolution.GetProject (id.ProjectId)); string currentText = data.Text; foreach (var change in changes) { delta -= change.Span.Length - change.NewText.Length; var startOffset = change.Span.Start - delta; if (projection != null) { int originalOffset; if (projection.TryConvertFromProjectionToOriginal (startOffset, out originalOffset)) startOffset = originalOffset; } string str; if (change.NewText.Length == 0) { str = formatter.FormatText (mp.Policies, currentText, TextSegment.FromBounds (Math.Max (0, startOffset - 1), Math.Min (data.Length, startOffset + 1))); } else { str = formatter.FormatText (mp.Policies, currentText, new TextSegment (startOffset, change.NewText.Length)); } data.ReplaceText (startOffset, change.NewText.Length, str); } } data.Save (); if (projection != null) { await UpdateProjectionsDocuments (document, data); } else { OnDocumentTextChanged (id, new MonoDevelopSourceText (data), PreservationMode.PreserveValue); } FileService.NotifyFileChanged (filePath); } else { var formatter = CodeFormatterService.GetFormatter (data.MimeType); var documentContext = IdeApp.Workbench.Documents.FirstOrDefault (d => FilePath.PathComparer.Compare (d.FileName, filePath) == 0); var root = await projectChanges.NewProject.GetDocument (id).GetSyntaxRootAsync (); var annotatedNode = root.DescendantNodesAndSelf ().FirstOrDefault (n => n.HasAnnotation (TypeSystemService.InsertionModeAnnotation)); SyntaxToken? renameTokenOpt = root.GetAnnotatedNodesAndTokens (Microsoft.CodeAnalysis.CodeActions.RenameAnnotation.Kind) .Where (s => s.IsToken) .Select (s => s.AsToken ()) .Cast<SyntaxToken?> () .FirstOrDefault (); if (documentContext != null) { var editor = (TextEditor)data; await Runtime.RunInMainThread (async () => { using (var undo = editor.OpenUndoGroup ()) { var oldVersion = editor.Version; delta = ApplyChanges (projection, data, changes); var versionBeforeFormat = editor.Version; if (formatter.SupportsOnTheFlyFormatting) { foreach (var change in changes) { delta -= change.Span.Length - change.NewText.Length; var startOffset = change.Span.Start - delta; if (projection != null) { int originalOffset; if (projection.TryConvertFromProjectionToOriginal (startOffset, out originalOffset)) startOffset = originalOffset; } if (change.NewText.Length == 0) { formatter.OnTheFlyFormat (editor, documentContext, TextSegment.FromBounds (Math.Max (0, startOffset - 1), Math.Min (data.Length, startOffset + 1))); } else { formatter.OnTheFlyFormat (editor, documentContext, new TextSegment (startOffset, change.NewText.Length)); } } } if (annotatedNode != null && GetInsertionPoints != null) { IdeApp.Workbench.Documents.First (d => d.FileName == editor.FileName).Select (); var formattedVersion = editor.Version; int startOffset = versionBeforeFormat.MoveOffsetTo (editor.Version, annotatedNode.Span.Start); int endOffset = versionBeforeFormat.MoveOffsetTo (editor.Version, annotatedNode.Span.End); // alway whole line start & delimiter var startLine = editor.GetLineByOffset (startOffset); startOffset = startLine.Offset; var endLine = editor.GetLineByOffset (endOffset); endOffset = endLine.EndOffsetIncludingDelimiter + 1; var insertionCursorSegment = TextSegment.FromBounds (startOffset, endOffset); string textToInsert = editor.GetTextAt (insertionCursorSegment).TrimEnd (); editor.RemoveText (insertionCursorSegment); var insertionPoints = await GetInsertionPoints (editor, editor.CaretOffset); if (insertionPoints.Count == 0) { // Just to get sure if no insertion points -> go back to the formatted version. var textChanges = editor.Version.GetChangesTo (formattedVersion).ToList (); using (var undo2 = editor.OpenUndoGroup ()) { foreach (var v in textChanges) { editor.ReplaceText (v.Offset, v.RemovalLength, v.InsertedText); } } return; } string insertionModeOperation; const int CSharpMethodKind = 8875; bool isMethod = annotatedNode.RawKind == CSharpMethodKind; if (!isMethod) { // atm only for generate field/property : remove all new lines generated & just insert the plain node. // for methods it's not so easy because of "extract code" changes. foreach (var v in editor.Version.GetChangesTo (oldVersion).ToList ()) { editor.ReplaceText (v.Offset, v.RemovalLength, v.InsertedText); } } switch (annotatedNode.RawKind) { case 8873: // C# field insertionModeOperation = GettextCatalog.GetString ("Insert Field"); break; case CSharpMethodKind: insertionModeOperation = GettextCatalog.GetString ("Insert Method"); break; case 8892: // C# property insertionModeOperation = GettextCatalog.GetString ("Insert Property"); break; default: insertionModeOperation = GettextCatalog.GetString ("Insert Code"); break; } var options = new InsertionModeOptions ( insertionModeOperation, insertionPoints, point => { if (!point.Success) return; point.InsertionPoint.Insert (editor, textToInsert); } ); options.ModeExitedAction += delegate (InsertionCursorEventArgs args) { if (!args.Success) { var textChanges = editor.Version.GetChangesTo (oldVersion).ToList (); using (var undo2 = editor.OpenUndoGroup ()) { foreach (var v in textChanges) { editor.ReplaceText (v.Offset, v.RemovalLength, v.InsertedText); } } } }; for (int i = 0; i < insertionPoints.Count; i++) { if (insertionPoints [i].Location.Line < editor.CaretLine) { options.FirstSelectedInsertionPoint = Math.Min (isMethod ? i + 1 : i, insertionPoints.Count - 1); } else { break; } } options.ModeExitedAction += delegate { if (renameTokenOpt.HasValue) StartRenameSession (editor, documentContext, versionBeforeFormat, renameTokenOpt.Value); }; editor.StartInsertionMode (options); } } }); } if (projection != null) { await UpdateProjectionsDocuments (document, data); } else { OnDocumentTextChanged (id, new MonoDevelopSourceText (data.CreateDocumentSnapshot ()), PreservationMode.PreserveValue); } await Runtime.RunInMainThread (() => { if (IdeApp.Workbench != null) foreach (var w in IdeApp.Workbench.Documents) w.StartReparseThread (); }); } }
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(); } }
// 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(); } }
internal void Update(SourceText newText) { var buffer = GetTextBuffer(); using (var edit = buffer.CreateEdit(EditOptions.DefaultMinimalChange, 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(); } }
protected override void ApplyDocumentTextChanged (DocumentId id, SourceText text) { var document = GetDocument (id); if (document == null) return; bool isOpen; var filePath = document.FilePath; var data = TextFileProvider.Instance.GetTextEditorData (filePath, out isOpen); // Guard against already done changes in linked files. // This shouldn't happen but the roslyn merging seems not to be working correctly in all cases :/ if (document.GetLinkedDocumentIds ().Length > 0 && isOpen && !(text.GetType ().FullName == "Microsoft.CodeAnalysis.Text.ChangedText")) { return; } SourceText formerText; if (changedFiles.TryGetValue (filePath, out formerText)) { if (formerText.Length == text.Length && formerText.ToString () == text.ToString ()) return; } changedFiles [filePath] = text; Projection projection = null; foreach (var entry in ProjectionList) { var p = entry.Projections.FirstOrDefault (proj => FilePath.PathComparer.Equals (proj.Document.FileName, filePath)); if (p != null) { filePath = entry.File.FilePath; projection = p; break; } } SourceText oldFile; if (!isOpen || !document.TryGetText (out oldFile)) { oldFile = new MonoDevelopSourceText (data); } var changes = text.GetTextChanges (oldFile).OrderByDescending (c => c.Span.Start).ToList (); int delta = 0; if (!isOpen) { delta = ApplyChanges (projection, data, changes); var formatter = CodeFormatterService.GetFormatter (data.MimeType); var mp = GetMonoProject (CurrentSolution.GetProject (id.ProjectId)); string currentText = data.Text; foreach (var change in changes) { delta -= change.Span.Length - change.NewText.Length; var startOffset = change.Span.Start - delta; if (projection != null) { int originalOffset; if (projection.TryConvertFromProjectionToOriginal (startOffset, out originalOffset)) startOffset = originalOffset; } string str; if (change.NewText.Length == 0) { str = formatter.FormatText (mp.Policies, currentText, TextSegment.FromBounds (Math.Max (0, startOffset - 1), Math.Min (data.Length, startOffset + 1))); } else { str = formatter.FormatText (mp.Policies, currentText, new TextSegment (startOffset, change.NewText.Length)); } data.ReplaceText (startOffset, change.NewText.Length, str); } data.Save (); OnDocumentTextChanged (id, new MonoDevelopSourceText (data), PreservationMode.PreserveValue); FileService.NotifyFileChanged (filePath); } else { var formatter = CodeFormatterService.GetFormatter (data.MimeType); var documentContext = IdeApp.Workbench.Documents.FirstOrDefault (d => FilePath.PathComparer.Compare (d.FileName, filePath) == 0); if (documentContext != null) { var editor = (TextEditor)data; using (var undo = editor.OpenUndoGroup ()) { delta = ApplyChanges (projection, data, changes); foreach (var change in changes) { delta -= change.Span.Length - change.NewText.Length; var startOffset = change.Span.Start - delta; if (projection != null) { int originalOffset; if (projection.TryConvertFromProjectionToOriginal (startOffset, out originalOffset)) startOffset = originalOffset; } if (change.NewText.Length == 0) { formatter.OnTheFlyFormat (editor, documentContext, TextSegment.FromBounds (Math.Max (0, startOffset - 1), Math.Min (data.Length, startOffset + 1))); } else { formatter.OnTheFlyFormat (editor, documentContext, new TextSegment (startOffset, change.NewText.Length)); } } } } OnDocumentTextChanged (id, new MonoDevelopSourceText(data.CreateDocumentSnapshot ()), PreservationMode.PreserveValue); Runtime.RunInMainThread (() => { if (IdeApp.Workbench != null) foreach (var w in IdeApp.Workbench.Documents) w.StartReparseThread (); }); } }
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(); } }
public void UpdateText(SourceText newText) { var subjectBuffer = (IProjectionBuffer)this.GetOpenTextBuffer(); var originalSnapshot = subjectBuffer.CurrentSnapshot; var originalText = originalSnapshot.AsText(); var changes = newText.GetTextChanges(originalText); IEnumerable<int> affectedVisibleSpanIndices = null; var editorVisibleSpansInOriginal = SharedPools.Default<List<TextSpan>>().AllocateAndClear(); try { var originalDocument = _workspace.CurrentSolution.GetDocument(this.Id); editorVisibleSpansInOriginal.AddRange(GetEditorVisibleSpans()); var newChanges = FilterTextChanges(originalText, editorVisibleSpansInOriginal, changes).ToList(); if (newChanges.Count == 0) { // no change to apply return; } ApplyChanges(subjectBuffer, newChanges, editorVisibleSpansInOriginal, out affectedVisibleSpanIndices); AdjustIndentation(subjectBuffer, affectedVisibleSpanIndices); } finally { SharedPools.Default<HashSet<int>>().ClearAndFree((HashSet<int>)affectedVisibleSpanIndices); SharedPools.Default<List<TextSpan>>().ClearAndFree(editorVisibleSpansInOriginal); } }