diff --git a/Nodejs/Product/Nodejs/Intellisense/VsProjectAnalyzer.cs b/Nodejs/Product/Nodejs/Intellisense/VsProjectAnalyzer.cs index 9e5d62882..956f0bc28 100644 --- a/Nodejs/Product/Nodejs/Intellisense/VsProjectAnalyzer.cs +++ b/Nodejs/Product/Nodejs/Intellisense/VsProjectAnalyzer.cs @@ -91,6 +91,9 @@ sealed partial class VsProjectAnalyzer : IDisposable { private readonly TaskProvider _defaultTaskProvider = CreateDefaultTaskProvider(); + internal static readonly string[] _emptyCompletionContextKeywords = new string[] { + "var", "function", "const", "let" + }; #if FALSE private readonly UnresolvedImportSquiggleProvider _unresolvedSquiggles; #endif @@ -156,9 +159,9 @@ private void CreateNewAnalyzer(AnalysisLimits limits) { _fullyLoaded = true; } - private bool ShouldEnqueue() { - return _analysisLevel != AnalysisLevel.None && _analysisLevel != AnalysisLevel.Preview; - } + private bool ShouldEnqueue() { + return _analysisLevel != AnalysisLevel.None && _analysisLevel != AnalysisLevel.Preview; + } #region Public API @@ -257,7 +260,7 @@ public void RemoveBuffer(ITextBuffer buffer) { } BufferParser bufferParser; - if (!buffer.Properties.TryGetProperty(typeof(BufferParser), out bufferParser)) { + if (!buffer.Properties.TryGetProperty(typeof(BufferParser), out bufferParser)) { return; } @@ -323,7 +326,7 @@ private void AnalyzeFile(string path, bool reportErrors, ProjectItem originating if (!reportErrors) { TaskProvider.Clear(item.Entry, ParserTaskMoniker); } - + if ((_reparseDateTime != null && new FileInfo(path).LastWriteTime > _reparseDateTime.Value) || (reportErrors && !item.ReportErrors) || (item.Entry.Module == null || item.Entry.Unit == null)) { @@ -378,21 +381,21 @@ public void AddPackageJson(string packageJsonPath) { AddPackageJson(packageJsonPath, (string)mainFile, dependencyList); } } - } - - private static List GetDependencyListFromJson(Dictionary json, params string[] dependencyTypes) { - var allDependencies = new List(); - foreach (var type in dependencyTypes) { - object dependencies; - json.TryGetValue(type, out dependencies); - var dep = dependencies as Dictionary; - if (dep != null) { - allDependencies.AddRange(dep.Keys.ToList()); - } - } - return allDependencies; - } - + } + + private static List GetDependencyListFromJson(Dictionary json, params string[] dependencyTypes) { + var allDependencies = new List(); + foreach (var type in dependencyTypes) { + object dependencies; + json.TryGetValue(type, out dependencies); + var dep = dependencies as Dictionary; + if (dep != null) { + allDependencies.AddRange(dep.Keys.ToList()); + } + } + return allDependencies; + } + public void AddPackageJson(string path, string mainFile, List dependencies) { if (!_fullyLoaded) { lock (_loadingDeltas) { @@ -1191,17 +1194,17 @@ private static CompletionAnalysis TrySpecialCompletions(ITextSnapshot snapshot, if (range != null) { start = range.Value.Start; } - } - - // Get the classifiers from beginning of the line to the beginning of snapSpan. - // The contents of snapSpan differ depending on what is determined in - // CompletionSource.GetApplicableSpan. - // - // In the case of: - // var myIdentifier - // the applicable span will be "myIdentifier", so GetClassificationSpans will operate on "var " - // - // In the case of comments and string literals, the applicable span will be empty, + } + + // Get the classifiers from beginning of the line to the beginning of snapSpan. + // The contents of snapSpan differ depending on what is determined in + // CompletionSource.GetApplicableSpan. + // + // In the case of: + // var myIdentifier + // the applicable span will be "myIdentifier", so GetClassificationSpans will operate on "var " + // + // In the case of comments and string literals, the applicable span will be empty, // so snapSpan.Start will occur at the current cursor position. var tokens = classifier.GetClassificationSpans(new SnapshotSpan(start.GetContainingLine().Start, snapSpan.Start)); if (tokens.Count > 0) { @@ -1210,8 +1213,9 @@ private static CompletionAnalysis TrySpecialCompletions(ITextSnapshot snapshot, if (lastClass.ClassificationType == classifier.Provider.Comment || lastClass.ClassificationType == classifier.Provider.StringLiteral || - (lastClass.ClassificationType == classifier.Provider.Keyword && lastClass.Span.GetText() == "var")) { - // No completions in comments, strings, or directly after "var" keywords. + (lastClass.ClassificationType == classifier.Provider.Keyword && + _emptyCompletionContextKeywords.Contains(lastClass.Span.GetText()))) { + // No completions in comments, strings, or directly after certain keywords. return CompletionAnalysis.EmptyCompletionContext; } return null; @@ -1288,7 +1292,7 @@ private void OnErrorRemoved(string path) { private void ClearParserTasks(IProjectEntry entry) { if (entry != null) { TaskProvider.Clear(entry, ParserTaskMoniker); - + bool changed; lock (_hasParseErrors) { changed = _hasParseErrors.Remove(entry); diff --git a/Nodejs/Tests/Core.UI/BasicIntellisense.cs b/Nodejs/Tests/Core.UI/BasicIntellisense.cs index c9f126369..17ea0091c 100644 --- a/Nodejs/Tests/Core.UI/BasicIntellisense.cs +++ b/Nodejs/Tests/Core.UI/BasicIntellisense.cs @@ -17,6 +17,7 @@ using System; using System.Linq; using System.Windows; +using Microsoft.NodejsTools.Intellisense; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.Text; @@ -272,29 +273,31 @@ public void IntellisenseAfterMultiLineComment() { /// [TestMethod, Priority(0), TestCategory("Core")] [HostType("VSTestHost")] - public void IntellisenseAfterVarKeyword() { - var project = Project("IntellisenseAfterVarKeywordTest", - Compile("server", "var c \r\nexports.var = 3; exports.var ") - ); - - using (var solution = project.Generate().ToVs()) { - var server = solution.OpenItem("IntellisenseAfterVarKeywordTest", "server.js"); - - server.MoveCaret(1, 4); - Keyboard.Type(Keyboard.CtrlSpace.ToString()); - using (var sh = server.WaitForSession(true)) { } - - server.MoveCaret(1, 6); - Keyboard.Type(Keyboard.CtrlSpace.ToString()); - server.AssertNoIntellisenseSession(); - - server.MoveCaret(1, 7); - Keyboard.Type(Keyboard.CtrlSpace.ToString()); - using (var sh = server.WaitForSession(true)) { } - - server.MoveCaret(2, 30); - Keyboard.Type(Keyboard.CtrlSpace.ToString()); - using (var sh = server.WaitForSession(true)) { } + public void IntellisenseAfterEmptyCompletionContextKeywords() { + foreach (var keyword in VsProjectAnalyzer._emptyCompletionContextKeywords) { + var project = Project("IntellisenseAfterEmptyCompletionContextKeywordTest", + Compile("server", String.Format("{0} c \r\nexports.{0} = 3; exports.{0} ", keyword)) + ); + + using (var solution = project.Generate().ToVs()) { + var server = solution.OpenItem("IntellisenseAfterEmptyCompletionContextKeywordTest", "server.js"); + + server.MoveCaret(1, keyword.Length + 1); + Keyboard.Type(Keyboard.CtrlSpace.ToString()); + using (var sh = server.WaitForSession(true)) { } + + server.MoveCaret(1, keyword.Length + 3); + Keyboard.Type(Keyboard.CtrlSpace.ToString()); + server.AssertNoIntellisenseSession(); + + server.MoveCaret(1, keyword.Length + 4); + Keyboard.Type(Keyboard.CtrlSpace.ToString()); + using (var sh = server.WaitForSession(true)) { } + + server.MoveCaret(2, keyword.Length*2 + 24); + Keyboard.Type(Keyboard.CtrlSpace.ToString()); + using (var sh = server.WaitForSession(true)) { } + } } }