/// <param name="kind">The type of template tag we are processing</param> /// <param name="templateText">The text of the template tag which we are offering a completion in</param> /// <param name="templateStart">The offset in the buffer where the template starts</param> /// <param name="triggerPoint">The point in the buffer where the completion was triggered</param> internal CompletionSet GetCompletionSet(CompletionOptions options, VsProjectAnalyzer analyzer, TemplateTokenKind kind, string templateText, int templateStart, SnapshotPoint triggerPoint, out ITrackingSpan applicableSpan) { int position = triggerPoint.Position - templateStart; IEnumerable <CompletionInfo> tags; IDjangoCompletionContext context; applicableSpan = GetWordSpan(templateText, templateStart, triggerPoint); switch (kind) { case TemplateTokenKind.Block: var block = DjangoBlock.Parse(templateText); if (block != null) { if (position <= block.ParseInfo.Start + block.ParseInfo.Command.Length) { // we are completing before the command // TODO: Return a new set of tags? Do nothing? Do this based upon ctrl-space? tags = FilterBlocks(CompletionInfo.ToCompletionInfo(analyzer.GetTags(), StandardGlyphGroup.GlyphKeyword), triggerPoint); } else { // we are in the arguments, let the block handle the completions context = new ProjectBlockCompletionContext(analyzer, _buffer); tags = block.GetCompletions(context, position); } } else { // no tag entered yet, provide the known list of tags. tags = FilterBlocks(CompletionInfo.ToCompletionInfo(analyzer.GetTags(), StandardGlyphGroup.GlyphKeyword), triggerPoint); } break; case TemplateTokenKind.Variable: var variable = DjangoVariable.Parse(templateText); context = new ProjectBlockCompletionContext(analyzer, _buffer); if (variable != null) { tags = variable.GetCompletions(context, position); } else { // show variable names tags = CompletionInfo.ToCompletionInfo(context.Variables, StandardGlyphGroup.GlyphGroupVariable); } break; default: throw new InvalidOperationException(); } var completions = tags .OrderBy(tag => tag.DisplayText, StringComparer.OrdinalIgnoreCase) .Select(tag => new DynamicallyVisibleCompletion( tag.DisplayText, tag.InsertionText, StripDocumentation(tag.Documentation), _glyphService.GetGlyph(tag.Glyph, StandardGlyphItem.GlyphItemPublic), "tag")); return(new FuzzyCompletionSet( "PythonDjangoTags", "Django Tags", applicableSpan, completions, options, CompletionComparer.UnderscoresLast)); }
public static DjangoForBlock Parse(BlockParseInfo parseInfo) { var words = parseInfo.Args.Split(' '); int inStart = -1; int inOffset = 0, inIndex = -1; var definitions = new List <Tuple <string, int> >(); for (int i = 0; i < words.Length; i++) { var word = words[i]; if (word == "in") { inStart = inOffset + parseInfo.Start + parseInfo.Command.Length; inIndex = i; break; } else if (words[i].IndexOfAny(NewLines) != -1) { // unterminated tag break; } if (!String.IsNullOrEmpty(word)) { definitions.Add(new Tuple <string, int>(word, inOffset + parseInfo.Start + parseInfo.Command.Length)); } inOffset += words[i].Length + 1; } // parse the arguments... int reversedStart = -1; DjangoVariable variable = null; int argsEnd = -1; if (inIndex != -1) { string filterText = ""; argsEnd = inStart + "in".Length + 1; for (int i = inIndex + 1; i < words.Length; i++) { int nlStart = words[i].IndexOfAny(NewLines); string trimmed = words[i]; if (nlStart != -1) { trimmed = words[i].Substring(0, nlStart); } if (i != inIndex + 1 && trimmed == words[i]) // if we trimmed we don't have an extra space { filterText += " "; argsEnd += 1; } if (trimmed == "reversed") { reversedStart = argsEnd; break; } filterText += trimmed; argsEnd += trimmed.Length; if (trimmed != words[i]) { // unterminated tag break; } } var trimmedFilter = filterText.TrimStart(' '); variable = DjangoVariable.Parse(trimmedFilter, inStart + "in".Length + 1 + filterText.Length - trimmedFilter.Length); } return(new DjangoForBlock(parseInfo, inStart, variable, argsEnd, reversedStart, definitions.ToArray())); }