private static CustomCompletionSet MergeCompletionSets(IList <CompletionSet> completionSets, CustomCompletionSet newCompletions) { var htmlCompletionsSet = completionSets.First(); // if we are in an element with tagPrefix, VS adds all HTML elements with the same prefix in the completion - we don't want it var originalCompletions = htmlCompletionsSet.Completions.Where(c => !c.DisplayText.Contains(":")); // merge var mergedCompletionSet = new CustomCompletionSet( htmlCompletionsSet.Moniker, htmlCompletionsSet.DisplayName, htmlCompletionsSet.ApplicableTo, newCompletions.Completions.Concat(originalCompletions).OrderBy(n => n.DisplayText).Distinct(CompletionEqualityComparer.Instance), htmlCompletionsSet.CompletionBuilders); completionSets.Remove(htmlCompletionsSet); return(mergedCompletionSet); }
public void AugmentCompletionSession(ICompletionSession session, IList <CompletionSet> completionSets) { var completionSetsCount = completionSets.Count; var tokens = classifier.Tokens; if (tokens != null) { // find current token var cursorPosition = session.TextView.Caret.Position.BufferPosition; var currentTokenIndex = FindCurrentTokenIndex(tokens, cursorPosition.Position); if (currentTokenIndex >= 0) { // prepare the context var currentToken = classifier.Tokens[currentTokenIndex]; var items = Enumerable.Empty <SimpleDothtmlCompletion>(); var context = GetCompletionContext(session); context.CompletionSession = session; context.CurrentTokenIndex = currentTokenIndex; context.CurrentNode = parser.Root.FindNodeByPosition(cursorPosition.Position - 1); var combineWithHtmlCompletions = false; int?applicableToStartPosition = null; TriggerPoint triggerPoint = TriggerPoint.None; if (currentToken.Type == DothtmlTokenType.DirectiveStart) { // directive name completion triggerPoint = TriggerPoint.DirectiveName; items = sourceProvider.CompletionProviders.Where(p => p.TriggerPoint == triggerPoint).SelectMany(p => p.GetItems(context)); } else if (currentToken.Type == DothtmlTokenType.DirectiveValue) { // directive value triggerPoint = TriggerPoint.DirectiveValue; // prepare applicable start position to include value text tag in the intellisense selection to replace applicableToStartPosition = tokens[currentTokenIndex].StartPosition; items = sourceProvider.CompletionProviders.Where(p => p.TriggerPoint == triggerPoint).SelectMany(p => p.GetItems(context)); } else if (currentToken.Type == DothtmlTokenType.OpenTag || IsTagName(currentTokenIndex, tokens)) { // element name triggerPoint = TriggerPoint.TagName; items = sourceProvider.CompletionProviders.Where(p => p.TriggerPoint == triggerPoint).SelectMany(p => p.GetItems(context)); if (currentToken.Type != DothtmlTokenType.OpenTag) { applicableToStartPosition = tokens[GetTagNameStartTokenIndex(currentTokenIndex, tokens)].StartPosition; } combineWithHtmlCompletions = true; } else if (currentToken.Type == DothtmlTokenType.WhiteSpace || currentToken.Type == DothtmlTokenType.Text) { var previousToken = GetPreviousToken(currentTokenIndex, tokens, new[] { DothtmlTokenType.WhiteSpace }); if (context.CurrentNode?.Tokens.All(all => all.Type == DothtmlTokenType.WhiteSpace || (all.Type == DothtmlTokenType.Text && string.IsNullOrWhiteSpace(all.Text))) == true) { session.Dismiss(); return; } // prepare applicable start position to include current text tag in the intellisense selection to replace if (currentToken.Type == DothtmlTokenType.Text) { applicableToStartPosition = tokens[currentTokenIndex].StartPosition; } if (context.CurrentNode is DothtmlDirectiveNode && previousToken?.Type == DothtmlTokenType.DirectiveName) { // directive value triggerPoint = TriggerPoint.DirectiveValue; items = sourceProvider.CompletionProviders.Where(p => p.TriggerPoint == triggerPoint).SelectMany(p => p.GetItems(context)); } else if (previousToken?.Type == DothtmlTokenType.OpenBinding) { // binding name triggerPoint = TriggerPoint.BindingName; items = sourceProvider.CompletionProviders.Where(p => p.TriggerPoint == triggerPoint).SelectMany(p => p.GetItems(context)); } else if (context.CurrentNode is DothtmlElementNode || context.CurrentNode is DothtmlAttributeNode) { // attribute name triggerPoint = TriggerPoint.TagAttributeName; items = sourceProvider.CompletionProviders.Where(p => p.TriggerPoint == triggerPoint).SelectMany(p => p.GetItems(context)); combineWithHtmlCompletions = sourceProvider.CompletionProviders.OfType <MainTagAttributeNameCompletionProvider>().Single().CombineWithHtmlCompletions; } else if (previousToken?.Type == DothtmlTokenType.SingleQuote || previousToken?.Type == DothtmlTokenType.DoubleQuote || previousToken?.Type == DothtmlTokenType.Equals) { // attribute value triggerPoint = TriggerPoint.TagAttributeValue; items = sourceProvider.CompletionProviders.Where(p => p.TriggerPoint == triggerPoint).SelectMany(p => p.GetItems(context)); combineWithHtmlCompletions = true; } } else if (currentToken.Type == DothtmlTokenType.SingleQuote || currentToken.Type == DothtmlTokenType.DoubleQuote || currentToken.Type == DothtmlTokenType.Equals) { // attribute value triggerPoint = TriggerPoint.TagAttributeValue; items = sourceProvider.CompletionProviders.Where(p => p.TriggerPoint == triggerPoint).SelectMany(p => p.GetItems(context)); combineWithHtmlCompletions = true; } else if (currentToken.Type == DothtmlTokenType.OpenBinding) { // binding name triggerPoint = TriggerPoint.BindingName; items = sourceProvider.CompletionProviders.Where(p => p.TriggerPoint == triggerPoint).SelectMany(p => p.GetItems(context)); } else if (currentToken.Type == DothtmlTokenType.Colon) { if (context.CurrentNode is DothtmlBindingNode) { // binding value triggerPoint = TriggerPoint.BindingValue; items = sourceProvider.CompletionProviders.Where(p => p.TriggerPoint == triggerPoint).SelectMany(p => p.GetItems(context)); } else { // element name triggerPoint = TriggerPoint.TagName; items = sourceProvider.CompletionProviders.Where(p => p.TriggerPoint == triggerPoint).SelectMany(p => p.GetItems(context)); combineWithHtmlCompletions = true; } } var results = items.OrderBy(v => v.DisplayText).Distinct(CompletionEqualityComparer.Instance).ToList(); // handle duplicate sessions (sometimes this method is called twice (e.g. when space key is pressed) so we need to make sure that we'll display only one session lock (activeSessions) { if (activeSessions.Count > 0) { activeSessions.ToList().ForEach(fe => fe.Dismiss()); } activeSessions.Add(session); session.Dismissed += (s, a) => { lock (activeSessions) { activeSessions.Remove((ICompletionSession)s); } }; session.Committed += (s, a) => { lock (activeSessions) { activeSessions.Remove((ICompletionSession)s); } }; } if (results.Any()) { // show the session var newCompletionSet = new CustomCompletionSet("dotVVM", "dotVVM", FindTokenSpanAtPosition(session, applicableToStartPosition ?? -1), results, null); if (combineWithHtmlCompletions && completionSets.Any()) { newCompletionSet = MergeCompletionSets(completionSets, newCompletionSet); } else { completionSets.Clear(); } completionSets.Add(newCompletionSet); } } } }