/// <summary> /// Returns the quick info for the <see cref="SyntaxEditor"/> at the specified offset. /// </summary> /// <param name="syntaxEditor">The <see cref="SyntaxEditor"/> to examine.</param> /// <param name="offset">The offset to examine. The offset is updated to the start of the context.</param> /// <returns>The quick info for the <see cref="SyntaxEditor"/> at the specified offset.</returns> private string GetQuickInfo(ActiproSoftware.SyntaxEditor.SyntaxEditor syntaxEditor, ref int offset) { Document document = syntaxEditor.Document; // Get the identifier at the offset, if any TextStream stream = syntaxEditor.Document.GetTextStream(offset); if (!stream.IsAtTokenStart) { stream.GoToCurrentTokenStart(); } offset = stream.Offset; // Check to see if we're over a warning SpanIndicatorLayer warningLayer = document.SpanIndicatorLayers[WarningLayerId]; if (warningLayer != null) { var sb = new StringBuilder(); var range = new TextRange(offset, offset + 1); SpanIndicator[] spans = warningLayer.GetIndicatorsForTextRange(range); foreach (LuatWarningSpanIndicator span in spans) { LuatWarning[] warnings = span.Warnings.Filter(warning => warning.TextRange.OverlapsWith(range)); var groupedWarnings = warnings.GroupItems(a => a.Message, a => a.Script); foreach (KeyValuePair <string, LuatScript[]> groupedWarning in groupedWarnings) { if (sb.Length > 0) { sb.Append("<br /><br />"); } sb.Append("Context: "); sb.Append("<b>"); sb.Append(groupedWarning.Value.ToCommaSeperatedList(a => a.Name)); sb.Append("</b>"); sb.Append("<br />"); sb.Append(groupedWarning.Key); } } if (sb.Length > 0) { return(sb.ToString()); } } // Get the containing node var cu = syntaxEditor.Document.SemanticParseData as CompilationUnit; if (cu == null) { return(null); } var qi = cu.FindNodeRecursive <IQuickInfoProvider>(stream.Offset); if (qi == null) { return(null); } return(FormatText(qi.QuickInfo)); }