예제 #1
0
        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);
        }
예제 #2
0
        public void UpdateText(SourceText newText)
        {
            _updatding = true;
            _editor.Document.BeginUpdate();
            var caret  = _editor.CaretOffset;
            var offset = 0;

            try
            {
                var changes = newText.GetTextChanges(_currentText);

                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;
                var carretOffset = caret + offset;
                if (carretOffset < 0)
                {
                    carretOffset = 0;
                }
                if (carretOffset > newText.Length)
                {
                    carretOffset = newText.Length;
                }
                _editor.CaretOffset = carretOffset;
                _editor.Document.EndUpdate();
            }
        }
예제 #3
0
            private void PushTextChanges(Document oldDocument, Document newDocument)
            {
                // this pushes text changes to the remote side if it can.
                // this is purely perf optimization. whether this pushing text change
                // worked or not doesn't affect feature's functionality.
                //
                // this basically see whether it can cheaply find out text changes
                // between 2 snapshots, if it can, it will send out that text changes to
                // remote side.
                //
                // the remote side, once got the text change, will again see whether
                // it can use that text change information without any high cost and
                // create new snapshot from it.
                //
                // otherwise, it will do the normal behavior of getting full text from
                // VS side. this optimization saves times we need to do full text
                // synchronization for typing scenario.

                SourceText oldText = null;
                SourceText newText = null;

                if ((oldDocument.TryGetText(out oldText) == false) ||
                    (newDocument.TryGetText(out newText) == false))
                {
                    // we only support case where text already exist
                    return;
                }

                // get text changes
                var textChanges = newText.GetTextChanges(oldText);

                if (textChanges.Count == 0)
                {
                    // no changes
                    return;
                }

                // whole document case
                if (textChanges.Count == 1 && textChanges[0].Span.Length == oldText.Length)
                {
                    // no benefit here. pulling from remote host is more efficient
                    return;
                }

                // only cancelled when remote host gets shutdown
                var token = Listener.BeginAsyncOperation(nameof(PushTextChanges));

                _textChangeQueue.ScheduleTask(async() =>
                {
                    var client = await _service.Workspace.TryGetRemoteHostClientAsync(CancellationToken).ConfigureAwait(false);
                    if (client == null)
                    {
                        return;
                    }

                    var state = await oldDocument.State.GetStateChecksumsAsync(CancellationToken).ConfigureAwait(false);

                    await client.TryRunRemoteAsync(
                        WellKnownRemoteHostServices.RemoteHostService, nameof(IRemoteHostService.SynchronizeTextAsync),
                        new object[] { oldDocument.Id, state.Text, textChanges }, CancellationToken).ConfigureAwait(false);
                }, CancellationToken).CompletesAsyncOperation(token);
            }
예제 #4
0
        protected override 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 => 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);
            var oldFile = isOpen ? document.GetTextAsync().Result : 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();
                        }
                    }
                });
            }
        }
예제 #5
0
 public override IReadOnlyList <TextChange> GetTextChanges(SourceText oldText) => _sourceText.GetTextChanges(oldText);
예제 #6
0
        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();
                        }
                    }
                });
            }
        }
        internal List <UnchangedSegment> GetReuseMapTo(SourceText newVersion)
        {
            SourceText oldVersion = this.Version;

            if (oldVersion == null || newVersion == null)
            {
                return(null);
            }
//			if (!oldVersion.BelongsToSameDocumentAs(newVersion))
//				return null;
            List <UnchangedSegment> reuseMap = new List <UnchangedSegment>();

            reuseMap.Add(new UnchangedSegment(0, 0, this.TextLength));
            foreach (var change in oldVersion.GetTextChanges(newVersion))
            {
                bool needsSegmentRemoval = false;
                var  insertionLength     = change.NewText != null ? change.NewText.Length : 0;
                for (int i = 0; i < reuseMap.Count; i++)
                {
                    UnchangedSegment segment = reuseMap[i];
                    if (segment.NewOffset + segment.Length <= change.Span.Start)
                    {
                        // change is completely after this segment
                        continue;
                    }
                    if (change.Span.End <= segment.NewOffset)
                    {
                        // change is completely before this segment
                        segment.NewOffset += insertionLength - change.Span.Length;
                        reuseMap[i]        = segment;
                        continue;
                    }
                    // Change is overlapping segment.
                    // Split segment into two parts: the part before change, and the part after change.
                    var segmentBefore = new UnchangedSegment(segment.OldOffset, segment.NewOffset, change.Span.Start - segment.NewOffset);
                    Debug.Assert(segmentBefore.Length < segment.Length);

                    int lengthAtEnd  = segment.NewOffset + segment.Length - (change.Span.End);
                    var segmentAfter = new UnchangedSegment(
                        segment.OldOffset + segment.Length - lengthAtEnd,
                        change.Span.Start + insertionLength,
                        lengthAtEnd);
                    Debug.Assert(segmentAfter.Length < segment.Length);
                    Debug.Assert(segmentBefore.Length + segmentAfter.Length <= segment.Length);
                    Debug.Assert(segmentBefore.NewOffset + segmentBefore.Length <= segmentAfter.NewOffset);
                    Debug.Assert(segmentBefore.OldOffset + segmentBefore.Length <= segmentAfter.OldOffset);
                    if (segmentBefore.Length > 0 && segmentAfter.Length > 0)
                    {
                        reuseMap[i] = segmentBefore;
                        reuseMap.Insert(++i, segmentAfter);
                    }
                    else if (segmentBefore.Length > 0)
                    {
                        reuseMap[i] = segmentBefore;
                    }
                    else
                    {
                        reuseMap[i] = segmentAfter;
                        if (segmentAfter.Length <= 0)
                        {
                            needsSegmentRemoval = true;
                        }
                    }
                }
                if (needsSegmentRemoval)
                {
                    reuseMap.RemoveAll(s => s.Length <= 0);
                }
            }
            return(reuseMap);
        }