Beispiel #1
0
        // routine(s) called by the FileContentManager upon updating a file

        /// <summary>
        /// Attempts to compute an incremental update for the change specified by start, count and newText, and updates file accordingly.
        /// The given argument newText replaces the entire lines from start to (but not including) start + count.
        /// If the given change is null, then (only) the currently queued unprocessed changes are processed.
        /// Throws an ArgumentNullException if file is null.
        /// Any other exceptions should be throws (and caught, and possibly re-thrown) during the updating.
        /// </summary>
        internal static void UpdateScopeTacking(this FileContentManager file, TextDocumentContentChangeEvent change)
        {
            if (file == null)
            {
                throw new ArgumentNullException(nameof(file));
            }

            /// <summary>
            /// Replaces the lines in the range [start, end] with those for the given text.
            /// </summary>
            void ComputeUpdate(int start, int end, string text)
            {
                QsCompilerError.Verify(start >= 0 && end >= start && end < file.NrLines(), "invalid range for update");

                // since both LF and CR in VS cause a line break on their own,
                // we need to check if the change causes subequent CR LF to merge into a single line break
                if (text.StartsWith(Utils.LF) && start > 0 && file.GetLine(start - 1).Text.EndsWith(Utils.CR))
                {
                    text = file.GetLine(--start).Text + text;
                }

                // we need to check if the change causes the next line to merge with the (last) changed line
                if (end + 1 < file.NrLines() && !Utils.EndOfLine.Match(text).Success)
                {
                    text = text + file.GetLine(++end).Text;
                }

                var newLines = Utils.SplitLines(text);
                var count    = end - start + 1;

                // note that the last line in the file won't end with a line break,
                // and is hence only captured by SplitLines if it is not empty
                // -> we therefore manually add the last line in the file if it is empty
                if (newLines.Length == 0 || // the case if the file will be empty after the update
                    (start + count == file.NrLines() && Utils.EndOfLine.Match(newLines.Last()).Success))
                {
                    newLines = newLines.Concat(new string[] { String.Empty }).ToArray();
                }

                QsCompilerError.Verify(newLines.Any(), "should have at least one line to replace");
                file.Update(start, count, newLines);
            }

            file.SyncRoot.EnterUpgradeableReadLock();
            try
            {
                // process the currently queued changes if necessary
                if (file.DequeueUnprocessedChanges(out int start, out string text))
                {
                    ComputeUpdate(start, start, text);
                }

                // process the given change if necessary
                if (change != null)
                {
                    ComputeUpdate(change.Range.Start.Line, change.Range.End.Line, Utils.GetTextChangedLines(file, change));
                }
            }
            finally { file.SyncRoot.ExitUpgradeableReadLock(); }
        }
Beispiel #2
0
        internal static void ApplyEdit(TextDocumentContentChangeEvent change, ref List <string> content)
        {
            if (!content.Any())
            {
                throw new ArgumentException("the given content has to have at least on line");
            }

            Assert.IsTrue(IsValidRange(change.Range) && change.Text != null);
            Assert.IsTrue(change.Range.End.Line < content.Count());
            Assert.IsTrue(change.Range.Start.Character <= content[change.Range.Start.Line].Length);
            Assert.IsTrue(change.Range.End.Character <= content[change.Range.End.Line].Length);

            var(startLine, startChar) = (change.Range.Start.Line, change.Range.Start.Character);
            var(endLine, endChar)     = (change.Range.End.Line, change.Range.End.Character);

            var newText = string.Concat(content[startLine].Substring(0, startChar), change.Text, content[endLine].Substring(endChar));

            if (startLine > 0)
            {
                newText = content[--startLine] + newText;
            }
            if (endLine + 1 < content.Count)
            {
                newText = newText + content[++endLine];
            }
            var lineChanges = Builder.SplitLines(newText);

            if (lineChanges.Length == 0 || (endLine + 1 == content.Count() && Builder.EndOfLine.Match(lineChanges.Last()).Success))
            {
                lineChanges = lineChanges.Concat(new string[] { string.Empty }).ToArray();
            }

            content.RemoveRange(startLine, endLine - startLine + 1);
            content.InsertRange(startLine, lineChanges);
        }
Beispiel #3
0
 public static PapCommon.ScriptTextChange ToScriptTextChange(this TextDocumentContentChangeEvent changeEvent)
 {
     return(new PapCommon.ScriptTextChange()
     {
         Range = changeEvent.Range.ToRange(),
         RangeLength = changeEvent.RangeLength,
         Text = changeEvent.Text
     });
 }
Beispiel #4
0
        private static string ApplyTextChange(string text, TextDocumentContentChangeEvent change, CancellationToken cancellationToken)
        {
            if (change.Range == null)
            {
                throw new InvalidOperationException("the range of the change must not be null");
            }
            int absoluteStart = change.Range.Start.ToAbsolutePosition(text, cancellationToken);
            int absoluteEnd   = change.Range.End.ToAbsolutePosition(text, cancellationToken);

            return(text[..absoluteStart] + change.Text + text[absoluteEnd..]);
 public override void OnDidSaveTextDocument(DidSaveTextDocumentParams parameters)
 {
     if (parameters.text != null)
     {
         DidChangeTextDocumentParams    dctdp = new DidChangeTextDocumentParams(parameters.textDocument.uri);
         TextDocumentContentChangeEvent tdcce = new TextDocumentContentChangeEvent();
         tdcce.text           = parameters.text;
         dctdp.contentChanges = new TextDocumentContentChangeEvent[] { tdcce };
         OnDidChangeTextDocument(dctdp);
     }
 }
            private Position GetPositionAtEndOfAppliedChange(TextDocumentContentChangeEvent change)
            {
                var changeStart     = change.Range.Start;
                var changeEof       = change.Text.GetEofPosition(_cancellationToken);
                var characterOffset = changeEof.Character;

                if (changeEof.Line == 0)
                {
                    characterOffset = changeStart.Character + changeEof.Character;
                }
                return(new Position(changeStart.Line + changeEof.Line, characterOffset));
            }
Beispiel #7
0
 private void Apply(TextDocumentItem document, TextDocumentContentChangeEvent ev)
 {
     if (ev.range != null)
     {
         var startPos = GetPosition(document, ev.range.start);
         var endPos   = GetPosition(document, ev.range.end);
         document.text = document.text.Substring(0, startPos) + ev.text + document.text.Substring(endPos);
     }
     else
     {
         document.text = ev.text;
     }
 }
Beispiel #8
0
 private void Apply(TextDocumentItem document, TextDocumentContentChangeEvent ev)
 {
     if (ev.range != null)
     {
         var startPos = GetPosition(document.text, (int)ev.range.start.line, (int)ev.range.start.character);
         var endPos   = GetPosition(document.text, (int)ev.range.end.line, (int)ev.range.end.character);
         var newText  = document.text.Substring(0, startPos) + ev.text + document.text.Substring(endPos);
         document.text = newText;
     }
     else
     {
         document.text = ev.text;
     }
 }
 private static void Apply(TextDocumentItem document, TextDocumentContentChangeEvent ev)
 {
     if (ev.Range != null)
     {
         var startPos = GetPosition(document.Text, (int)ev.Range.Start.Line, (int)ev.Range.Start.Character);
         var endPos   = GetPosition(document.Text, (int)ev.Range.End.Line, (int)ev.Range.End.Character);
         var newText  = document.Text.Substring(0, startPos) + ev.Text + document.Text.Substring(endPos);
         document.Text = newText;
     }
     else
     {
         document.Text = ev.Text;
     }
 }
Beispiel #10
0
        /// <summary>
        /// Return a string with the new content of the (entire) lines in the range [start, end] where start and end are
        /// the start and end line of the given change.
        /// </summary>
        /// <exception cref="ArgumentException">The range is invalid.</exception>
        /// <exception cref="ArgumentOutOfRangeException">The range is not contained in <paramref name="file"/>.</exception>
        internal static string GetTextChangedLines(FileContentManager file, TextDocumentContentChangeEvent change)
        {
            if (!file.ContainsRange(change.Range.ToQSharp()))
            {
                throw new ArgumentOutOfRangeException(nameof(change)); // range can be empty
            }

            var first   = file.GetLine(change.Range.Start.Line).Text;
            var last    = file.GetLine(change.Range.End.Line).Text;
            var prepend = first.Substring(0, change.Range.Start.Character);
            var append  = last.Substring(change.Range.End.Character);

            return(string.Concat(prepend, change.Text, append));
        }
Beispiel #11
0
        public void SimpleTest(string expected)
        {
            var model = new TextDocumentContentChangeEvent {
                Range       = new Range(new Position(1, 2), new Position(3, 4)),
                RangeLength = 12,
                Text        = "abc"
            };
            var result = Fixture.SerializeObject(model);

            result.Should().Be(expected);

            var deresult = new LspSerializer(ClientVersion.Lsp3).DeserializeObject <TextDocumentContentChangeEvent>(expected);

            deresult.Should().BeEquivalentTo(model, x => x.UsingStructuralRecordEquality());
        }
Beispiel #12
0
        private void Apply(TextDocument document, TextDocumentContentChangeEvent changeEvent)
        {
            if (changeEvent.Range != null)
            {
                int    startPos = GetPosition(document.Text, changeEvent.Range.Start.Line, changeEvent.Range.Start.Character);
                int    endPos   = GetPosition(document.Text, changeEvent.Range.End.Line, changeEvent.Range.End.Character);
                string newText  = document.Text.Substring(0, startPos) + changeEvent.Text + document.Text.Substring(endPos);

                document.Text = newText;
            }
            else
            {
                document.Text = changeEvent.Text;
            }
        }
Beispiel #13
0
        public void SimpleTest(string expected)
        {
            var model = new TextDocumentContentChangeEvent()
            {
                Range       = new Range(new Position(1, 2), new Position(3, 4)),
                RangeLength = 12,
                Text        = "abc"
            };
            var result = Fixture.SerializeObject(model);

            result.Should().Be(expected);

            var deresult = JsonConvert.DeserializeObject <TextDocumentContentChangeEvent>(expected);

            deresult.ShouldBeEquivalentTo(model);
        }
Beispiel #14
0
        /// the last edit will always be a delete all
        internal TextDocumentContentChangeEvent[] MakeRandomEdits(int nrEdits, ref List <string> content, int expectedNrLines, bool withLanguageKeywords)
        {
            var edits = new TextDocumentContentChangeEvent[nrEdits];

            if (nrEdits == 0)
            {
                return(edits);
            }
            for (var i = 0; i < edits.Length - 1; ++i)
            {
                edits[i] = this.GetRandomEdit(content, expectedNrLines, withLanguageKeywords);
                TestUtils.ApplyEdit(edits[i], ref content);
            }
            edits[edits.Length - 1] = DeleteAll(content);
            TestUtils.ApplyEdit(edits.Last(), ref content);
            return(edits);
        }
Beispiel #15
0
        /// <summary>
        /// Return a string with the new content of the (entire) lines in the range [start, end] where start and end are the start and end line of the given change.
        /// Verifies that the given change is consistent with the given file - i.e. the range is a valid range in file, and the text is not null, and
        /// throws the correspoding exceptions if this is not the case.
        /// </summary>
        internal static string GetTextChangedLines(FileContentManager file, TextDocumentContentChangeEvent change)
        {
            if (!IsValidRange(change.Range, file))
            {
                throw new ArgumentOutOfRangeException(nameof(change)); // range can be empty
            }
            if (change.Text == null)
            {
                throw new ArgumentNullException(nameof(change.Text), "the given text change is null");
            }

            var first   = file.GetLine(change.Range.Start.Line).Text;
            var last    = file.GetLine(change.Range.End.Line).Text;
            var prepend = first.Substring(0, change.Range.Start.Character);
            var append  = last.Substring(change.Range.End.Character);

            return(string.Concat(prepend, change.Text, append));
        }
Beispiel #16
0
        public void Patch(System.Uri uri, TextDocumentContentChangeEvent change)
        {
            if (change.Range is null)
            {
                // non-incremental/full buffer synchronization
                this.Put(uri, change.Text);
                return;
            }

            var buffer = _buffers.GetOrAdd(uri.ToString(), (key) => new StringBuilder());
            var text   = buffer.ToString();
            int start  = BufferPosition(text, change.Range.Start);

            if (start != -1)
            {
                buffer.Remove(start, change.RangeLength);
                buffer.Insert(start, change.Text);
            }
        }
Beispiel #17
0
        public void ApplyContentChanges_SingleChange()
        {
            // Arrange
            var endpoint   = new RazorDocumentSynchronizationEndpoint(Dispatcher, DocumentResolver, ProjectService, LoggerFactory);
            var sourceText = SourceText.From("Hello World");
            var change     = new TextDocumentContentChangeEvent()
            {
                Range       = new Range(new Position(0, 5), new Position(0, 5)),
                RangeLength = 0,
                Text        = "!"
            };

            // Act
            var result = endpoint.ApplyContentChanges(new[] { change }, sourceText);

            // Assert
            var resultString = GetString(result);

            Assert.Equal("Hello! World", resultString);
        }
Beispiel #18
0
        public void Empty_at_0_0()
        {
            TextDocumentContentChangeEvent change = new TextDocumentContentChangeEvent
            {
                Text  = "",
                Range = new Range
                {
                    Start = new Position {
                        Line = 0, Character = 0
                    },
                    End = new Position {
                        Line = 0, Character = 0
                    }
                },
                RangeLength = 0
            };

            f.Apply(change);

            Assert.AreEqual(cleanSource, cleanSource);
        }
Beispiel #19
0
        public void InsertOneCharAt_1_0()
        {
            TextDocumentContentChangeEvent change = new TextDocumentContentChangeEvent
            {
                Text  = "a",
                Range = new Range
                {
                    Start = new Position {
                        Line = 1, Character = 0
                    },
                    End = new Position {
                        Line = 1, Character = 0
                    }
                },
                RangeLength = 0
            };

            f.Apply(change);

            Assert.AreEqual(cleanSource.Insert(2, "a"), f.Sourcecode);
        }
Beispiel #20
0
        public void PressingRETURNat_15_15()
        {
            TextDocumentContentChangeEvent change = new TextDocumentContentChangeEvent
            {
                Text  = "",
                Range = new Range
                {
                    Start = new Position {
                        Line = 15, Character = 14
                    },
                    End = new Position {
                        Line = 15, Character = 15
                    }
                },
                RangeLength = 1
            };

            f.Apply(change);

            Assert.AreEqual(cleanSource.Remove(228, 1), f.Sourcecode);
        }
Beispiel #21
0
        public void InsertTextAt_15_15()
        {
            TextDocumentContentChangeEvent change = new TextDocumentContentChangeEvent
            {
                Text  = "abc",
                Range = new Range
                {
                    Start = new Position {
                        Line = 15, Character = 15
                    },
                    End = new Position {
                        Line = 15, Character = 15
                    }
                },
                RangeLength = 0
            };

            f.Apply(change);

            Assert.AreEqual(cleanSource.Insert(229, "abc"), f.Sourcecode);
        }
Beispiel #22
0
        public void RemovingLine_15_IncludingNewLine()
        {
            TextDocumentContentChangeEvent change = new TextDocumentContentChangeEvent
            {
                Text  = "",
                Range = new Range
                {
                    Start = new Position {
                        Line = 15, Character = 0
                    },
                    End = new Position {
                        Line = 16, Character = 0
                    }
                },
                RangeLength = 37
            };

            f.Apply(change);

            Assert.AreEqual(cleanSource.Remove(214, 37), f.Sourcecode);
        }
Beispiel #23
0
        public void RemovingRangeAt_15_15()
        {
            TextDocumentContentChangeEvent change = new TextDocumentContentChangeEvent
            {
                Text  = "",
                Range = new Range
                {
                    Start = new Position {
                        Line = 15, Character = 15
                    },
                    End = new Position {
                        Line = 15, Character = 20
                    }
                },
                RangeLength = 5
            };

            f.Apply(change);

            Assert.AreEqual(cleanSource.Remove(229, 5), f.Sourcecode);
        }
Beispiel #24
0
        public void RemovingLine_1()
        {
            TextDocumentContentChangeEvent change = new TextDocumentContentChangeEvent
            {
                Text  = "",
                Range = new Range
                {
                    Start = new Position {
                        Line = 0, Character = 0
                    },
                    End = new Position {
                        Line = 1, Character = 0
                    }
                },
                RangeLength = 2
            };

            f.Apply(change);

            Assert.AreEqual(cleanSource.Remove(0, 2), f.Sourcecode);
        }
Beispiel #25
0
        public void ReplaceLine_15()
        {
            TextDocumentContentChangeEvent change = new TextDocumentContentChangeEvent
            {
                Text  = "baba",
                Range = new Range
                {
                    Start = new Position {
                        Line = 15, Character = 1
                    },
                    End = new Position {
                        Line = 15, Character = 30
                    }
                },
                RangeLength = 29
            };

            f.Apply(change);

            Assert.AreEqual(cleanSource.Remove(215, 29).Insert(215, "baba"), f.Sourcecode);
        }
Beispiel #26
0
        public async Task Handle_DidChangeTextDocument_UpdatesDocument()
        {
            // Arrange
            var documentPath     = "C:/path/to/document.cshtml";
            var sourceText       = SourceText.From("<p>");
            var documentResolver = CreateDocumentResolver(documentPath, sourceText);
            var projectService   = new Mock <RazorProjectService>(MockBehavior.Strict);

            projectService.Setup(service => service.UpdateDocument(It.IsAny <string>(), It.IsAny <SourceText>(), It.IsAny <long>()))
            .Callback <string, SourceText, long>((path, text, version) =>
            {
                var resultString = GetString(text);
                Assert.Equal("<p></p>", resultString);
                Assert.Equal(documentPath, path);
                Assert.Equal(1337, version);
            });
            var endpoint = new RazorDocumentSynchronizationEndpoint(Dispatcher, documentResolver, projectService.Object, LoggerFactory);
            var change   = new TextDocumentContentChangeEvent()
            {
                Range       = new Range(new Position(0, 3), new Position(0, 3)),
                RangeLength = 0,
                Text        = "</p>"
            };
            var request = new DidChangeTextDocumentParams()
            {
                ContentChanges = new Container <TextDocumentContentChangeEvent>(change),
                TextDocument   = new VersionedTextDocumentIdentifier()
                {
                    Uri     = new Uri(documentPath),
                    Version = 1337,
                }
            };

            // Act
            await Task.Run(() => endpoint.Handle(request, default));

            // Assert
            projectService.VerifyAll();
        }
Beispiel #27
0
        public void InsertAt_2_8()
        {
            TextDocumentContentChangeEvent change = new TextDocumentContentChangeEvent
            {
                Text  = "abc",
                Range = new Range
                {
                    Start = new Position {
                        Line = 2, Character = 8
                    },
                    End = new Position {
                        Line = 2, Character = 8
                    }
                },
                RangeLength = 0
            };

            f.Apply(change);

            const string expected = "class A {\r\n    var a:int;\r\n    consabctructor(){}\r\n}";

            Assert.AreEqual(expected, f.Sourcecode);
        }
Beispiel #28
0
        public void AppendText()
        {
            TextDocumentContentChangeEvent change = new TextDocumentContentChangeEvent
            {
                Text  = "//bla",
                Range = new Range
                {
                    Start = new Position {
                        Line = 3, Character = 1
                    },
                    End = new Position {
                        Line = 3, Character = 1
                    }
                },
                RangeLength = 0
            };

            f.Apply(change);

            const string expected = "class A {\r\n    var a:int;\r\n    constructor(){}\r\n}//bla";

            Assert.AreEqual(expected, f.Sourcecode);
        }
Beispiel #29
0
        public void Cut_All()
        {
            TextDocumentContentChangeEvent change = new TextDocumentContentChangeEvent
            {
                Text  = "",
                Range = new Range
                {
                    Start = new Position {
                        Line = 0, Character = 0
                    },
                    End = new Position {
                        Line = 3, Character = 1
                    }
                },
                RangeLength = 49
            };

            f.Apply(change);

            const string expected = @"";

            Assert.AreEqual(expected, f.Sourcecode);
        }
Beispiel #30
0
        public void Replace()
        {
            TextDocumentContentChangeEvent change = new TextDocumentContentChangeEvent
            {
                Text  = "var b:string;",
                Range = new Range
                {
                    Start = new Position {
                        Line = 1, Character = 4
                    },
                    End = new Position {
                        Line = 1, Character = 14
                    }
                },
                RangeLength = 10
            };

            f.Apply(change);

            const string expected = "class A {\r\n    var b:string;\r\n    constructor(){}\r\n}";

            Assert.AreEqual(expected, f.Sourcecode);
        }