From a8ba13b0e34e19d04842ae11117ce06f79484bcf Mon Sep 17 00:00:00 2001 From: Mikhail Arkhipov Date: Tue, 24 Sep 2019 17:45:52 -0700 Subject: [PATCH 1/9] Use proper scope --- .../Impl/Completion/TopLevelCompletion.cs | 22 ++++++++-------- src/LanguageServer/Test/CompletionTests.cs | 25 +++++++++++++++++++ 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/LanguageServer/Impl/Completion/TopLevelCompletion.cs b/src/LanguageServer/Impl/Completion/TopLevelCompletion.cs index 6a1e735a4..2dca6491f 100644 --- a/src/LanguageServer/Impl/Completion/TopLevelCompletion.cs +++ b/src/LanguageServer/Impl/Completion/TopLevelCompletion.cs @@ -61,16 +61,18 @@ public static CompletionResult GetCompletions(Node statement, ScopeStatement sco // Add possible function arguments. var finder = new ExpressionFinder(context.Ast, new FindExpressionOptions { Calls = true }); if (finder.GetExpression(context.Position) is CallExpression callExpr && callExpr.GetArgumentAtIndex(context.Ast, context.Position, out _)) { - var value = eval.GetValueFromExpression(callExpr.Target); - if (value?.GetPythonType() is IPythonFunctionType ft) { - var arguments = ft.Overloads.SelectMany(o => o.Parameters).Select(p => p?.Name) - .Where(n => !string.IsNullOrEmpty(n)) - .Distinct() - .Except(callExpr.Args.MaybeEnumerate().Select(a => a.Name).Where(n => !string.IsNullOrEmpty(n))) - .Select(n => CompletionItemSource.CreateCompletionItem($"{n}=", CompletionItemKind.Variable)) - .ToArray(); - - items = items.Concat(arguments).ToArray(); + using (context.Analysis.ExpressionEvaluator.OpenScope(context.Analysis.Document, scopeStatement)) { + var value = eval.GetValueFromExpression(callExpr.Target); + if (value?.GetPythonType() is IPythonFunctionType ft) { + var arguments = ft.Overloads.SelectMany(o => o.Parameters).Select(p => p?.Name) + .Where(n => !string.IsNullOrEmpty(n)) + .Distinct() + .Except(callExpr.Args.MaybeEnumerate().Select(a => a.Name).Where(n => !string.IsNullOrEmpty(n))) + .Select(n => CompletionItemSource.CreateCompletionItem($"{n}=", CompletionItemKind.Variable)) + .ToArray(); + + items = items.Concat(arguments).ToArray(); + } } } diff --git a/src/LanguageServer/Test/CompletionTests.cs b/src/LanguageServer/Test/CompletionTests.cs index 2839faca8..c5f963a60 100644 --- a/src/LanguageServer/Test/CompletionTests.cs +++ b/src/LanguageServer/Test/CompletionTests.cs @@ -1322,5 +1322,30 @@ public async Task FromImportPackageNoInitPy() { var names = comps.Completions.Select(c => c.label); names.Should().Contain(new[] { "sub1" }); } + + [TestMethod, Priority(0)] + public async Task InFunctionParameters() { + const string code = @" +class A: + def method(self, content=1): + return content + +class B: + def method(self): + a = A() + a.method() + +a = A() +a.method() +"; + var analysis = await GetAnalysisAsync(code); + var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion); + + var comps = cs.GetCompletions(analysis, new SourceLocation(9, 18)); + comps.Should().HaveLabels("content="); + + comps = cs.GetCompletions(analysis, new SourceLocation(12, 10)); + comps.Should().HaveLabels("content="); + } } } From 3f1c943289d8b912ea445a049e3e1ab129971cc9 Mon Sep 17 00:00:00 2001 From: Mikhail Arkhipov Date: Tue, 24 Sep 2019 18:12:18 -0700 Subject: [PATCH 2/9] Handle parameters of class ctor in completion --- src/Analysis/Ast/Impl/Extensions/MemberExtensions.cs | 8 ++++++++ .../Impl/Completion/TopLevelCompletion.cs | 3 ++- src/LanguageServer/Impl/Sources/SignatureSource.cs | 9 +-------- src/LanguageServer/Test/CompletionTests.cs | 12 ++++++++++-- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/Analysis/Ast/Impl/Extensions/MemberExtensions.cs b/src/Analysis/Ast/Impl/Extensions/MemberExtensions.cs index a68f91b51..213dde5d2 100644 --- a/src/Analysis/Ast/Impl/Extensions/MemberExtensions.cs +++ b/src/Analysis/Ast/Impl/Extensions/MemberExtensions.cs @@ -116,5 +116,13 @@ public static bool IsDeclaredAfterOrAt(this ILocatedMember lm, ILocatedMember ot => lm.IsDeclaredAfterOrAt(other.Location); public static bool IsDeclaredAfterOrAt(this ILocatedMember lm, Location loc) => lm.Location.IndexSpan.Start >= loc.IndexSpan.Start; + + public static IPythonFunctionType TryGetFunctionType(this IMember m) { + var t = m.GetPythonType(); + return t is IPythonClassType cls + ? cls.GetMember("__init__") + : t as IPythonFunctionType; + } + } } diff --git a/src/LanguageServer/Impl/Completion/TopLevelCompletion.cs b/src/LanguageServer/Impl/Completion/TopLevelCompletion.cs index 2dca6491f..fba314c1d 100644 --- a/src/LanguageServer/Impl/Completion/TopLevelCompletion.cs +++ b/src/LanguageServer/Impl/Completion/TopLevelCompletion.cs @@ -63,7 +63,8 @@ public static CompletionResult GetCompletions(Node statement, ScopeStatement sco if (finder.GetExpression(context.Position) is CallExpression callExpr && callExpr.GetArgumentAtIndex(context.Ast, context.Position, out _)) { using (context.Analysis.ExpressionEvaluator.OpenScope(context.Analysis.Document, scopeStatement)) { var value = eval.GetValueFromExpression(callExpr.Target); - if (value?.GetPythonType() is IPythonFunctionType ft) { + var ft = value.TryGetFunctionType(); + if (ft != null) { var arguments = ft.Overloads.SelectMany(o => o.Parameters).Select(p => p?.Name) .Where(n => !string.IsNullOrEmpty(n)) .Distinct() diff --git a/src/LanguageServer/Impl/Sources/SignatureSource.cs b/src/LanguageServer/Impl/Sources/SignatureSource.cs index dbde79d28..f92eb5824 100644 --- a/src/LanguageServer/Impl/Sources/SignatureSource.cs +++ b/src/LanguageServer/Impl/Sources/SignatureSource.cs @@ -64,14 +64,7 @@ public SignatureHelp GetSignature(IDocumentAnalysis analysis, SourceLocation loc } } - IPythonFunctionType ft; - - if (value is IPythonClassType cls) { - ft = cls.GetMember("__init__"); - } else { - ft = value?.GetPythonType(); - } - + var ft = value.TryGetFunctionType(); if (ft == null) { return null; } diff --git a/src/LanguageServer/Test/CompletionTests.cs b/src/LanguageServer/Test/CompletionTests.cs index c5f963a60..eb07031b3 100644 --- a/src/LanguageServer/Test/CompletionTests.cs +++ b/src/LanguageServer/Test/CompletionTests.cs @@ -1331,21 +1331,29 @@ def method(self, content=1): return content class B: + def __init__(self, ctorParam = 2): + pass + def method(self): a = A() a.method() a = A() a.method() + +b = B() "; var analysis = await GetAnalysisAsync(code); var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion); - var comps = cs.GetCompletions(analysis, new SourceLocation(9, 18)); + var comps = cs.GetCompletions(analysis, new SourceLocation(12, 18)); comps.Should().HaveLabels("content="); - comps = cs.GetCompletions(analysis, new SourceLocation(12, 10)); + comps = cs.GetCompletions(analysis, new SourceLocation(15, 10)); comps.Should().HaveLabels("content="); + + comps = cs.GetCompletions(analysis, new SourceLocation(17, 7)); + comps.Should().HaveLabels("ctorParam="); } } } From 41de21d4110e248e6f9f8808436abaf92aceaf77 Mon Sep 17 00:00:00 2001 From: Mikhail Arkhipov Date: Wed, 25 Sep 2019 12:38:53 -0700 Subject: [PATCH 3/9] Prevent document reload with old content --- .../Impl/Documents/Definitions/IDocument.cs | 4 +- .../Ast/Impl/Documents/DocumentBuffer.cs | 65 ++++++++++++++++++- .../Impl/Documents/RunningDocumentTable.cs | 7 +- src/Analysis/Ast/Impl/Modules/PythonModule.cs | 32 ++++----- src/Analysis/Ast/Test/DocumentBufferTests.cs | 31 ++++++--- 5 files changed, 104 insertions(+), 35 deletions(-) diff --git a/src/Analysis/Ast/Impl/Documents/Definitions/IDocument.cs b/src/Analysis/Ast/Impl/Documents/Definitions/IDocument.cs index dc1228ddb..4f6801dff 100644 --- a/src/Analysis/Ast/Impl/Documents/Definitions/IDocument.cs +++ b/src/Analysis/Ast/Impl/Documents/Definitions/IDocument.cs @@ -63,9 +63,9 @@ public interface IDocument: IPythonModule, IDisposable { void Update(IEnumerable changes); /// - /// Resets document buffer to the provided content or tries to load it if content is null, then parses and analyzes document. + /// Forces parse and analysis of the document. /// - void Reset(string content); + void Invalidate(); /// /// Provides collection of parsing errors, if any. diff --git a/src/Analysis/Ast/Impl/Documents/DocumentBuffer.cs b/src/Analysis/Ast/Impl/Documents/DocumentBuffer.cs index 21a2bdfb8..6dd93024d 100644 --- a/src/Analysis/Ast/Impl/Documents/DocumentBuffer.cs +++ b/src/Analysis/Ast/Impl/Documents/DocumentBuffer.cs @@ -13,7 +13,9 @@ // See the Apache Version 2.0 License for specific language governing // permissions and limitations under the License. +using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using Microsoft.Python.Parsing; @@ -23,6 +25,8 @@ internal sealed class DocumentBuffer { private readonly object _lock = new object(); private StringBuilder _sb = new StringBuilder(); private string _content; + private bool _contentDropped; + private bool _populated; public int Version { get; private set; } @@ -34,18 +38,65 @@ public string Text { } } - public void Reset(int version, string content) { + /// + /// Clear buffer content to save memory. + /// The buffer cannot be modified after this point. + /// + public void Clear() { lock (_lock) { - Version = version; + // Content is being dropped to save memory. + _content = string.Empty; + _sb = null; + _contentDropped = true; + } + } + + /// + /// Advances content version of the buffer without changing the contents. + /// Typically used to invalidate analysis. + /// + public void MarkChanged() { + lock (_lock) { + if (!_populated) { + throw new InvalidOperationException("Buffer is not populated."); + } + if (_contentDropped) { + throw new InvalidOperationException("Buffer content was dropped and cannot be updated."); + } + Version++; + } + } + + /// + /// Populates buffer with initial content. This can ony happen once. + /// + /// + public void Populate(string content) { + lock (_lock) { + // Buffer initial population. + if (_populated) { + throw new InvalidOperationException("Buffer is already populated."); + } + if (_contentDropped) { + throw new InvalidOperationException("Buffer content was dropped and cannot be updated."); + } + Version = 0; _content = content ?? string.Empty; _sb = null; + _populated = true; } } public void Update(IEnumerable changes) { lock (_lock) { + if (_contentDropped) { + throw new InvalidOperationException("Buffer content was dropped and cannot be updated."); + } + _sb = _sb ?? new StringBuilder(_content); + //var lastStart = int.MaxValue; + //var lastEnd = int.MaxValue; foreach (var change in changes) { // Every change may change where the lines end so in order // to correctly determine line/offsets we must re-split buffer @@ -59,12 +110,21 @@ public void Update(IEnumerable changes) { var start = NewLineLocation.LocationToIndex(lineLoc, change.ReplacedSpan.Start, _sb.Length); var end = NewLineLocation.LocationToIndex(lineLoc, change.ReplacedSpan.End, _sb.Length); + + //Debug.Assert(end >= start); + //Debug.Assert(start < lastStart); + //Debug.Assert(end < lastEnd); + //Debug.Assert(end <= lastStart); + if (end > start) { _sb.Remove(start, end - start); } if (!string.IsNullOrEmpty(change.InsertedText)) { _sb.Insert(start, change.InsertedText); } + + //lastStart = start; + //lastEnd = end; } Version++; @@ -98,7 +158,6 @@ public IEnumerable GetNewLineLocations() { if (i == _sb.Length - 1) { yield return new NewLineLocation(i + 1, NewLineKind.None); } - break; } } diff --git a/src/Analysis/Ast/Impl/Documents/RunningDocumentTable.cs b/src/Analysis/Ast/Impl/Documents/RunningDocumentTable.cs index 810632abc..4e855397c 100644 --- a/src/Analysis/Ast/Impl/Documents/RunningDocumentTable.cs +++ b/src/Analysis/Ast/Impl/Documents/RunningDocumentTable.cs @@ -106,7 +106,7 @@ public IDocument OpenDocument(Uri uri, string content, string filePath = null) { }; entry = CreateDocument(mco); } - justOpened = TryOpenDocument(entry, content); + justOpened = TryOpenDocument(entry); document = entry.Document; } @@ -217,7 +217,7 @@ public void ReloadAll() { } foreach (var (_, entry) in opened) { - entry.Document.Reset(null); + entry.Document.Invalidate(); } } @@ -277,10 +277,9 @@ private bool TryAddModulePath(ModuleCreationOptions mco) { return true; } - private bool TryOpenDocument(DocumentEntry entry, string content) { + private bool TryOpenDocument(DocumentEntry entry) { if (!entry.Document.IsOpen) { entry.Document.IsOpen = true; - entry.Document.Reset(content); entry.LockCount++; return true; } diff --git a/src/Analysis/Ast/Impl/Modules/PythonModule.cs b/src/Analysis/Ast/Impl/Modules/PythonModule.cs index af216a813..772d9938c 100644 --- a/src/Analysis/Ast/Impl/Modules/PythonModule.cs +++ b/src/Analysis/Ast/Impl/Modules/PythonModule.cs @@ -119,7 +119,7 @@ internal PythonModule(ModuleCreationOptions creationOptions, IServiceContainer s } IsPersistent = creationOptions.IsPersistent; - InitializeContent(creationOptions.Content, 0); + InitializeContent(creationOptions.Content); } #region IPythonType @@ -314,14 +314,11 @@ public void Update(IEnumerable changes) { Services.GetService().InvalidateAnalysis(this); } - public void Reset(string content) { + public void Invalidate() { lock (_syncObj) { - if (content != Content) { - ContentState = State.None; - InitializeContent(content, _buffer.Version + 1); - } + ContentState = State.None; + Parse(); } - Services.GetService().InvalidateAnalysis(this); } @@ -450,7 +447,7 @@ public void NotifyAnalysisComplete(IDocumentAnalysis analysis) { ContentState = State.Analyzed; if (ModuleType != ModuleType.User) { - _buffer.Reset(_buffer.Version, string.Empty); + _buffer.Clear(); } } @@ -486,7 +483,7 @@ public void AddAstNode(object o, Node n) { public void ClearContent() { lock (_syncObj) { if (ModuleType != ModuleType.User) { - _buffer.Reset(_buffer.Version, string.Empty); + _buffer.Clear(); _astMap.Clear(); } } @@ -514,18 +511,21 @@ protected virtual string LoadContent() { return null; // Keep content as null so module can be loaded later. } - private void InitializeContent(string content, int version) { + /// + /// Populates buffer with content. Content can either be provided, such as when + /// user opens the document or generated such as when module is compiled and + /// scraper will generate the content in the overridden LoadContent(). + /// + private void InitializeContent(string content) { lock (_syncObj) { - LoadContent(content, version); - - var startParse = ContentState < State.Parsing && (_parsingTask == null || version > 0); - if (startParse) { + LoadContent(content); + if (ContentState < State.Parsing && _parsingTask == null) { Parse(); } } } - private void LoadContent(string content, int version) { + private void LoadContent(string content) { if (ContentState < State.Loading) { try { if (IsPersistent) { @@ -533,7 +533,7 @@ private void LoadContent(string content, int version) { } else { content = content ?? LoadContent(); } - _buffer.Reset(version, content); + _buffer.Populate(content); ContentState = State.Loaded; } catch (IOException) { } catch (UnauthorizedAccessException) { } } diff --git a/src/Analysis/Ast/Test/DocumentBufferTests.cs b/src/Analysis/Ast/Test/DocumentBufferTests.cs index c0230ac18..f00533a67 100644 --- a/src/Analysis/Ast/Test/DocumentBufferTests.cs +++ b/src/Analysis/Ast/Test/DocumentBufferTests.cs @@ -29,7 +29,7 @@ public class DocumentBufferTests { [TestMethod, Priority(0)] public void BasicDocumentBuffer() { var doc = new DocumentBuffer(); - doc.Reset(0, @"def f(x): + doc.Populate(@"def f(x): return def g(y): @@ -77,7 +77,7 @@ def g(y): public void ResetDocumentBuffer() { var doc = new DocumentBuffer(); - doc.Reset(0, string.Empty); + doc.Populate(string.Empty); Assert.AreEqual(string.Empty, doc.Text); doc.Update(new[] { @@ -87,7 +87,7 @@ public void ResetDocumentBuffer() { Assert.AreEqual("text", doc.Text); Assert.AreEqual(1, doc.Version); - doc.Reset(0, @"abcdef"); + doc.Populate(@"abcdef"); Assert.AreEqual(@"abcdef", doc.Text); Assert.AreEqual(0, doc.Version); @@ -97,7 +97,7 @@ public void ResetDocumentBuffer() { public void ReplaceAllDocumentBuffer() { var doc = new DocumentBuffer(); - doc.Reset(0, string.Empty); + doc.Populate(string.Empty); Assert.AreEqual(string.Empty, doc.Text); doc.Update(new[] { @@ -134,7 +134,7 @@ public void ReplaceAllDocumentBuffer() { [TestMethod, Priority(0)] public void DeleteMultipleDisjoint() { var doc = new DocumentBuffer(); - doc.Reset(0, @" + doc.Populate(@" line1 line2 line3 @@ -157,7 +157,7 @@ public void DeleteMultipleDisjoint() { [TestMethod, Priority(0)] public void InsertMultipleDisjoint() { var doc = new DocumentBuffer(); - doc.Reset(0, @" + doc.Populate(@" line line line @@ -180,7 +180,7 @@ public void InsertMultipleDisjoint() { [TestMethod, Priority(0)] public void DeleteAcrossLines() { var doc = new DocumentBuffer(); - doc.Reset(0, @" + doc.Populate(@" line1 line2 line3 @@ -199,7 +199,7 @@ public void DeleteAcrossLines() { [TestMethod, Priority(0)] public void SequentialChanges() { var doc = new DocumentBuffer(); - doc.Reset(0, @" + doc.Populate(@" line1 line2 line3 @@ -218,7 +218,7 @@ public void SequentialChanges() { [TestMethod, Priority(0)] public void InsertTopToBottom() { var doc = new DocumentBuffer(); - doc.Reset(0, @"linelinelineline"); + doc.Populate(@"linelinelineline"); doc.Update(new[] { DocumentChange.Insert("\n", new SourceLocation(1, 1)), DocumentChange.Insert("1\n", new SourceLocation(2, 5)), @@ -233,7 +233,7 @@ public void InsertTopToBottom() { [DataTestMethod, Priority(0)] public void NewLines(string s, NewLineLocation[] expected) { var doc = new DocumentBuffer(); - doc.Reset(0, s); + doc.Populate(s); var nls = doc.GetNewLineLocations().ToArray(); for (var i = 0; i < nls.Length; i++) { Assert.AreEqual(nls[i].Kind, expected[i].Kind); @@ -281,7 +281,18 @@ public IEnumerable GetData(MethodInfo methodInfo) => new NewLineLocation(12, NewLineKind.LineFeed), new NewLineLocation(13, NewLineKind.None) } + }, + new object[] { + "\r\na\r\n\r\n\r\n \r\n", + new NewLineLocation[] { + new NewLineLocation(2, NewLineKind.CarriageReturnLineFeed), + new NewLineLocation(5, NewLineKind.CarriageReturnLineFeed), + new NewLineLocation(7, NewLineKind.CarriageReturnLineFeed), + new NewLineLocation(9, NewLineKind.CarriageReturnLineFeed), + new NewLineLocation(12, NewLineKind.CarriageReturnLineFeed), + new NewLineLocation(13, NewLineKind.None) } + } }; public string GetDisplayName(MethodInfo methodInfo, object[] data) => data != null ? $"{methodInfo.Name} ({FormatName((string)data[0])})" : null; From 5ed1c8200b043f6d8d7247f6935e9eefd2e7a542 Mon Sep 17 00:00:00 2001 From: Mikhail Arkhipov Date: Wed, 25 Sep 2019 12:47:37 -0700 Subject: [PATCH 4/9] Call MarkChanged --- src/Analysis/Ast/Impl/Modules/PythonModule.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Analysis/Ast/Impl/Modules/PythonModule.cs b/src/Analysis/Ast/Impl/Modules/PythonModule.cs index 772d9938c..5af1238fa 100644 --- a/src/Analysis/Ast/Impl/Modules/PythonModule.cs +++ b/src/Analysis/Ast/Impl/Modules/PythonModule.cs @@ -317,6 +317,7 @@ public void Update(IEnumerable changes) { public void Invalidate() { lock (_syncObj) { ContentState = State.None; + _buffer.MarkChanged(); Parse(); } Services.GetService().InvalidateAnalysis(this); From ee6480bcd7ec6e1be36e6675840cfb0869c733f4 Mon Sep 17 00:00:00 2001 From: Mikhail Arkhipov Date: Wed, 25 Sep 2019 13:01:02 -0700 Subject: [PATCH 5/9] Use Check --- .../Ast/Impl/Documents/DocumentBuffer.cs | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/Analysis/Ast/Impl/Documents/DocumentBuffer.cs b/src/Analysis/Ast/Impl/Documents/DocumentBuffer.cs index 6dd93024d..a6546db50 100644 --- a/src/Analysis/Ast/Impl/Documents/DocumentBuffer.cs +++ b/src/Analysis/Ast/Impl/Documents/DocumentBuffer.cs @@ -13,11 +13,10 @@ // See the Apache Version 2.0 License for specific language governing // permissions and limitations under the License. -using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Text; +using Microsoft.Python.Core.Diagnostics; using Microsoft.Python.Parsing; namespace Microsoft.Python.Analysis.Documents { @@ -57,12 +56,8 @@ public void Clear() { /// public void MarkChanged() { lock (_lock) { - if (!_populated) { - throw new InvalidOperationException("Buffer is not populated."); - } - if (_contentDropped) { - throw new InvalidOperationException("Buffer content was dropped and cannot be updated."); - } + Check.InvalidOperation(_populated, "Buffer is not populated."); + Check.InvalidOperation(!_contentDropped, "Buffer content was dropped and cannot be updated."); Version++; } } @@ -73,13 +68,8 @@ public void MarkChanged() { /// public void Populate(string content) { lock (_lock) { - // Buffer initial population. - if (_populated) { - throw new InvalidOperationException("Buffer is already populated."); - } - if (_contentDropped) { - throw new InvalidOperationException("Buffer content was dropped and cannot be updated."); - } + Check.InvalidOperation(_populated, "Buffer is already populated."); + Check.InvalidOperation(!_contentDropped, "Buffer content was dropped and cannot be updated."); Version = 0; _content = content ?? string.Empty; _sb = null; @@ -89,10 +79,7 @@ public void Populate(string content) { public void Update(IEnumerable changes) { lock (_lock) { - if (_contentDropped) { - throw new InvalidOperationException("Buffer content was dropped and cannot be updated."); - } - + Check.InvalidOperation(!_contentDropped, "Buffer content was dropped and cannot be updated."); _sb = _sb ?? new StringBuilder(_content); //var lastStart = int.MaxValue; From c9c247e9059a1ebe28b0f04f356416c5a9c5275c Mon Sep 17 00:00:00 2001 From: Mikhail Arkhipov Date: Wed, 25 Sep 2019 13:02:18 -0700 Subject: [PATCH 6/9] Condition --- src/Analysis/Ast/Impl/Documents/DocumentBuffer.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Analysis/Ast/Impl/Documents/DocumentBuffer.cs b/src/Analysis/Ast/Impl/Documents/DocumentBuffer.cs index a6546db50..b64270bdc 100644 --- a/src/Analysis/Ast/Impl/Documents/DocumentBuffer.cs +++ b/src/Analysis/Ast/Impl/Documents/DocumentBuffer.cs @@ -43,7 +43,6 @@ public string Text { /// public void Clear() { lock (_lock) { - // Content is being dropped to save memory. _content = string.Empty; _sb = null; _contentDropped = true; @@ -68,7 +67,7 @@ public void MarkChanged() { /// public void Populate(string content) { lock (_lock) { - Check.InvalidOperation(_populated, "Buffer is already populated."); + Check.InvalidOperation(!_populated, "Buffer is already populated."); Check.InvalidOperation(!_contentDropped, "Buffer content was dropped and cannot be updated."); Version = 0; _content = content ?? string.Empty; From 4a47bcaf8f297a718eaf26271cce210397944be6 Mon Sep 17 00:00:00 2001 From: Mikhail Arkhipov Date: Wed, 25 Sep 2019 13:03:55 -0700 Subject: [PATCH 7/9] Remove debug code --- src/Analysis/Ast/Impl/Documents/DocumentBuffer.cs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/Analysis/Ast/Impl/Documents/DocumentBuffer.cs b/src/Analysis/Ast/Impl/Documents/DocumentBuffer.cs index b64270bdc..e05536806 100644 --- a/src/Analysis/Ast/Impl/Documents/DocumentBuffer.cs +++ b/src/Analysis/Ast/Impl/Documents/DocumentBuffer.cs @@ -81,8 +81,6 @@ public void Update(IEnumerable changes) { Check.InvalidOperation(!_contentDropped, "Buffer content was dropped and cannot be updated."); _sb = _sb ?? new StringBuilder(_content); - //var lastStart = int.MaxValue; - //var lastEnd = int.MaxValue; foreach (var change in changes) { // Every change may change where the lines end so in order // to correctly determine line/offsets we must re-split buffer @@ -97,20 +95,13 @@ public void Update(IEnumerable changes) { var start = NewLineLocation.LocationToIndex(lineLoc, change.ReplacedSpan.Start, _sb.Length); var end = NewLineLocation.LocationToIndex(lineLoc, change.ReplacedSpan.End, _sb.Length); - //Debug.Assert(end >= start); - //Debug.Assert(start < lastStart); - //Debug.Assert(end < lastEnd); - //Debug.Assert(end <= lastStart); - if (end > start) { _sb.Remove(start, end - start); } + if (!string.IsNullOrEmpty(change.InsertedText)) { _sb.Insert(start, change.InsertedText); } - - //lastStart = start; - //lastEnd = end; } Version++; From 0b629122628fbc19ddb8a5936736b404581b23eb Mon Sep 17 00:00:00 2001 From: Mikhail Arkhipov Date: Wed, 25 Sep 2019 14:54:21 -0700 Subject: [PATCH 8/9] Update test --- src/Analysis/Ast/Test/DocumentBufferTests.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Analysis/Ast/Test/DocumentBufferTests.cs b/src/Analysis/Ast/Test/DocumentBufferTests.cs index f00533a67..c6e48b8a6 100644 --- a/src/Analysis/Ast/Test/DocumentBufferTests.cs +++ b/src/Analysis/Ast/Test/DocumentBufferTests.cs @@ -86,11 +86,6 @@ public void ResetDocumentBuffer() { Assert.AreEqual("text", doc.Text); Assert.AreEqual(1, doc.Version); - - doc.Populate(@"abcdef"); - - Assert.AreEqual(@"abcdef", doc.Text); - Assert.AreEqual(0, doc.Version); } [TestMethod, Priority(0)] From f82251415380be0c4ce8d29123e5c1f39d6fa7a6 Mon Sep 17 00:00:00 2001 From: Mikhail Arkhipov Date: Wed, 25 Sep 2019 17:29:33 -0700 Subject: [PATCH 9/9] Merge fix --- src/LanguageServer/Test/CompletionTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LanguageServer/Test/CompletionTests.cs b/src/LanguageServer/Test/CompletionTests.cs index 65d09310b..ec9d0a06e 100644 --- a/src/LanguageServer/Test/CompletionTests.cs +++ b/src/LanguageServer/Test/CompletionTests.cs @@ -1363,7 +1363,7 @@ def method(self): b = B() "; var analysis = await GetAnalysisAsync(code); - var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion); + var cs = new CompletionSource(new PlainTextDocumentationSource(), ServerSettings.completion, Services); var comps = cs.GetCompletions(analysis, new SourceLocation(12, 18)); comps.Should().HaveLabels("content=");