public void ResetDocumentBuffer() { var doc = new DocumentBuffer(); doc.Reset(0, ""); Assert.AreEqual("", doc.Text.ToString()); doc.Update(new[] { new DocumentChangeSet(0, 1, new[] { DocumentChange.Insert("text", SourceLocation.MinValue) }) }); Assert.AreEqual("text", doc.Text.ToString()); try { doc.Update(new[] { new DocumentChangeSet(1, 0, new[] { DocumentChange.Delete(SourceLocation.MinValue, SourceLocation.MinValue.AddColumns(4)) }) }); Assert.Fail("expected InvalidOperationException"); } catch (InvalidOperationException) { } Assert.AreEqual("text", doc.Text.ToString()); Assert.AreEqual(1, doc.Version); doc.Update(new[] { new DocumentChangeSet(1, 0, new[] { new DocumentChange { WholeBuffer = true } }) }); Assert.AreEqual("", doc.Text.ToString()); Assert.AreEqual(0, doc.Version); }
public async Task MultiPartDocument() { var s = await CreateServer(); var mod = await AddModule(s, "x = 1", "mod"); var modP2 = new Uri(mod, "#2"); var modP3 = new Uri(mod, "#3"); await AssertCompletion(s, mod, new[] { "x" }, Enumerable.Empty <string>()); Assert.AreEqual(Tuple.Create("y = 2", 1), await ApplyChange(s, modP2, DocumentChange.Insert("y = 2", SourceLocation.MinValue))); await s.WaitForCompleteAnalysisAsync(); await AssertCompletion(s, modP2, new[] { "x", "y" }, Enumerable.Empty <string>()); Assert.AreEqual(Tuple.Create("z = 3", 1), await ApplyChange(s, modP3, DocumentChange.Insert("z = 3", SourceLocation.MinValue))); await s.WaitForCompleteAnalysisAsync(); await AssertCompletion(s, modP3, new[] { "x", "y", "z" }, Enumerable.Empty <string>()); await AssertCompletion(s, mod, new[] { "x", "y", "z" }, Enumerable.Empty <string>()); await ApplyChange(s, mod, DocumentChange.Delete(SourceLocation.MinValue, SourceLocation.MinValue.AddColumns(5))); await s.WaitForCompleteAnalysisAsync(); await AssertCompletion(s, modP2, new[] { "y", "z" }, new[] { "x" }); await AssertCompletion(s, modP3, new[] { "y", "z" }, new[] { "x" }); }
private DocumentChange Delete(int oldStart, int oldEnd) { return(DocumentChange.Delete(new SourceSpan( _oldLines[oldStart].Info.SourceStart, _oldLines[oldEnd].Info.SourceEndIncludingLineBreak ))); }
public async Task ApplyChanges() { var s = await CreateServer(); var m = await AddModule(s, "", "mod"); Assert.AreEqual(Tuple.Create("x", 1), await ApplyChange(s, m, DocumentChange.Insert("x", new SourceLocation(1, 1)))); Assert.AreEqual(Tuple.Create("", 2), await ApplyChange(s, m, DocumentChange.Delete(new SourceLocation(1, 1), new SourceLocation(1, 2)))); Assert.AreEqual(Tuple.Create("y", 3), await ApplyChange(s, m, DocumentChange.Insert("y", new SourceLocation(1, 1)))); }
public void BasicDocumentBuffer() { var doc = new DocumentBuffer(); doc.Reset(0, @"def f(x): return def g(y): return y * 2 "); doc.Update(new DocumentChangeSet(0, 1, new[] { // We *should* batch adjacent insertions, but we should also // work fine even if we don't. Note that the insertion point // tracks backwards with previous insertions in the same version. // If each of these were in its own array, the location would // have to change for each. DocumentChange.Insert(")", new SourceLocation(2, 11)), DocumentChange.Insert("x", new SourceLocation(2, 11)), DocumentChange.Insert("(", new SourceLocation(2, 11)), DocumentChange.Insert("g", new SourceLocation(2, 11)), DocumentChange.Insert(" ", new SourceLocation(2, 11)) })); doc.Text.Should().Contain("return g(x)"); Assert.AreEqual(1, doc.Version); doc.Update(new[] { new DocumentChangeSet(1, 2, new [] { DocumentChange.Delete(new SourceLocation(2, 14), new SourceLocation(2, 15)) }), new DocumentChangeSet(2, 3, new [] { DocumentChange.Insert("x * 2", new SourceLocation(2, 14)) }) }); doc.Text.Should().Contain("return g(x * 2)"); doc.Update(new DocumentChangeSet(3, 4, new[] { DocumentChange.Replace(new SourceLocation(2, 18), new SourceLocation(2, 19), "300") })); doc.Text.Should().Contain("return g(x * 300)"); doc.Update(new DocumentChangeSet(4, 5, new[] { // Changes are out of order, but we should fix that automatically DocumentChange.Delete(new SourceLocation(2, 13), new SourceLocation(2, 22)), DocumentChange.Insert("#", new SourceLocation(2, 7)) })); doc.Text.Should().Contain("re#turn g"); }
public void SequentialChanges() { var doc = new DocumentBuffer(); doc.SetContent(@" line1 line2 line3 line4 "); doc.Update(new[] { DocumentChange.Delete(new SourceSpan(2, 5, 3, 5)), DocumentChange.Delete(new SourceSpan(3, 5, 4, 5)) }); Assert.AreEqual(@" line2 line4 ", doc.Text); }
public void DeleteAcrossLines() { var doc = new DocumentBuffer(); doc.Reset(0, @" line1 line2 line3 line4 "); doc.Update(new[] { DocumentChange.Delete(new SourceSpan(4, 5, 5, 5)), DocumentChange.Delete(new SourceSpan(2, 5, 3, 5)), }); Assert.AreEqual(@" line2 line4 ", doc.Text); }
private void ReplaceLines(List <DocumentChange> edits, int startOldLine, int endOldLine, int startNewLine, int endNewLine) { int oldLineCount = endOldLine - startOldLine; int newLineCount = endNewLine - startNewLine; // replace one line at a time instead of all of the lines at once so that we preserve breakpoints int excessNewLineStart = startNewLine - startOldLine; for (int i = startOldLine; i < endOldLine && i < (endNewLine - startNewLine + startOldLine); i++) { edits.Add( DocumentChange.Replace( _oldLines[i].SourceExtent, GetNewText(startNewLine + i - startOldLine) ) ); excessNewLineStart = startNewLine + i - startOldLine + 1; } if (oldLineCount > newLineCount) { // we end up w/ less lines, we need to delete some text edits.Add( DocumentChange.Delete(new SourceSpan( _oldLines[endOldLine - (oldLineCount - newLineCount)].SourceStart, _oldLines[endOldLine - 1].SourceEndIncludingLineBreak )) ); } else if (oldLineCount < newLineCount) { // we end up w/ more lines, we need to insert some text edits.Add( DocumentChange.Insert( string.Join( _newLine, _newLines.Skip(excessNewLineStart).Take(endNewLine - excessNewLineStart).Select(x => GetNewText(x.LineNo)) ) + _newLine, _oldLines[endOldLine - 1].SourceEndIncludingLineBreak ) ); } }
public void DeleteMultipleDisjoint() { var doc = new DocumentBuffer(); doc.SetContent(@" line1 line2 line3 line4 "); doc.Update(new[] { DocumentChange.Delete(new SourceSpan(5, 5, 5, 6)), DocumentChange.Delete(new SourceSpan(4, 5, 4, 6)), DocumentChange.Delete(new SourceSpan(3, 5, 3, 6)), DocumentChange.Delete(new SourceSpan(2, 5, 2, 6)) }); Assert.AreEqual(@" line line line line ", doc.Text); }
public IReadOnlyList <DocumentChange> ReplaceCode() { var edits = new List <DocumentChange>(); var oldLineMapping = new Dictionary <string, List <int> >(); // line to line # for (int i = 0; i < _oldLines.Count; i++) { List <int> lineInfo; if (!oldLineMapping.TryGetValue(GetOldText(i), out lineInfo)) { oldLineMapping[GetOldText(i)] = lineInfo = new List <int>(); } lineInfo.Add(i); } int curOldLine = 0; for (int curNewLine = 0; curOldLine < _oldLines.Count && curNewLine < _newLines.Count; curOldLine++) { if (GetOldText(curOldLine) == GetNewText(curNewLine)) { curNewLine++; continue; } bool replaced = false; // replace starts on this line, figure out where it ends... int startNewLine = curNewLine; for (curNewLine += 1; curNewLine < _newLines.Count; curNewLine++) { List <int> lines; if (oldLineMapping.TryGetValue(GetNewText(curNewLine), out lines)) { foreach (var matchingLineNo in lines) { if (matchingLineNo > curOldLine) { // Replace the lines from curOldLine to matchingLineNo-1 with the text // from startNewLine - curNewLine - 1. ReplaceLines(edits, curOldLine, matchingLineNo, startNewLine, curNewLine); replaced = true; curOldLine = matchingLineNo - 1; break; } } } if (replaced) { break; } } if (!replaced) { ReplaceLines(edits, curOldLine, _oldLines.Count, startNewLine, _newLines.Count); curOldLine = _oldLines.Count; break; } } if (curOldLine < _oldLines.Count) { // remove the remaining new lines edits.Add( DocumentChange.Delete(new SourceSpan( _oldLines[curOldLine].SourceStart, _oldLines[_oldLines.Count - 1].SourceEnd )) ); } return(edits.ToArray()); }