//===================================================================== /// <inheritdoc /> public IEnumerable <ITagSpan <NaturalTextTag> > GetTags(NormalizedSnapshotSpanCollection spans) { if (classifier == null || spans == null || spans.Count == 0) { yield break; } ITextSnapshot snapshot = spans[0].Snapshot; foreach (var snapshotSpan in spans) { Debug.Assert(snapshotSpan.Snapshot.TextBuffer == buffer); foreach (ClassificationSpan classificationSpan in classifier.GetClassificationSpans(snapshotSpan)) { string name = classificationSpan.ClassificationType.Classification.ToLowerInvariant(); switch (name) { case "md_bold": // Markers only, these contain nothing that can be spell checked case "md_header": case "md_html": case "md_italic": case "md_quote": case "keyword": break; default: // "md_code" will most likely contain a fair number of false reports but the // classification can be excluded if necessary through the configuration or // specific unwanted words through the Ignore Spelling directive. classificationCache.Add(name); if (!ignoredClassifications.Contains(name)) { yield return(new TagSpan <NaturalTextTag>(classificationSpan.Span, new NaturalTextTag())); } break; } } } }
//===================================================================== /// <inheritdoc /> public IEnumerable <ITagSpan <NaturalTextTag> > GetTags(NormalizedSnapshotSpanCollection spans) { bool preprocessorKeywordSeen = false, delimiterSeen = false; string elementName = null, attributeName = null, text; int pos; if (classifier == null || spans == null || spans.Count == 0) { yield break; } ITextSnapshot snapshot = spans[0].Snapshot; foreach (var snapshotSpan in spans) { Debug.Assert(snapshotSpan.Snapshot.TextBuffer == buffer); foreach (ClassificationSpan classificationSpan in classifier.GetClassificationSpans(snapshotSpan)) { string name = classificationSpan.ClassificationType.Classification.ToLowerInvariant(), originalName = name; // Do some conversion to make things simpler below switch (name) { case "sql string": // Skip the leading Unicode indicator if present var span = classificationSpan.Span; if (span.Length > 2 && span.GetText()[0] == 'N') { span = new SnapshotSpan(span.Snapshot, span.Start + 1, span.Length - 1); } classificationCache.Add(name); if (!ignoredClassifications.Contains(name)) { yield return(new TagSpan <NaturalTextTag>(span, new NaturalTextTag())); } continue; case "vb xml doc attribute": name = "attribute value"; break; // VS2015 is much more specific in classifying XML doc comment parts case "xml doc comment - delimiter": name = "xml delimiter"; break; case "xml doc comment - name": name = "xml name"; break; case "xml doc comment - attribute name": name = "xml attribute"; break; case "xml doc comment - attribute value": name = "attribute value"; break; case "xml doc comment - text": break; default: if (name == "identifier" || name.StartsWith("xml doc comment - ", StringComparison.Ordinal)) { continue; } break; } // As long as the opening and closing XML tags appear on the same line as the content, we // can skip spell checking of unwanted elements. if (name == "xml delimiter" || name == "xaml delimiter" || name == "vb xml doc tag" || name.StartsWith("vb xml delimiter", StringComparison.Ordinal)) { text = classificationSpan.Span.GetText(); if (text.IndexOf('/') != -1) { elementName = null; delimiterSeen = false; } else if (text.IndexOf('<') != -1) { delimiterSeen = true; } if (name == "vb xml doc tag" && delimiterSeen) { if (text.Length > 1 && text[0] == '<') { pos = text.IndexOf(' '); if (pos != -1) { elementName = text.Substring(1, pos - 1); } else { elementName = text.Substring(1, text.Length - 2); } } if (text.Length > 1 && text[text.Length - 1] == '=') { pos = text.IndexOf(' '); if (pos != -1) { attributeName = text.Substring(pos + 1, text.Length - pos - 2); } else { attributeName = text.Substring(0, text.Length - 1); } } } } if (delimiterSeen && (name == "xml name" || name == "xaml name" || name.StartsWith("vb xml name", StringComparison.Ordinal))) { elementName = classificationSpan.Span.GetText().Trim(); // Ignore any namespace prefix if (elementName.IndexOf(':') != -1) { elementName = elementName.Substring(elementName.IndexOf(':') + 1); } } // As long as the attribute value appears on the same line as the attribute name, we can // spell check attribute values if wanted. if (name.EndsWith(" attribute", StringComparison.Ordinal) || name.Contains("attribute name")) { // XAML attribute names may include leading and trailing white space attributeName = classificationSpan.Span.GetText().Trim(); // Ignore any namespace prefix if (attributeName.IndexOf(':') != -1) { attributeName = attributeName.Substring(attributeName.IndexOf(':') + 1); } } if ((name.Contains("comment") || name.Contains("string") || name.Contains("xml text") || name.Contains("xaml text") || name.Contains("attribute value")) && !name.Contains("xml doc tag")) { // If it's not a wanted attribute name, don't spell check its value if (attributeName != null && name.Contains("attribute value") && !spellCheckedXmlAttributes.Contains(attributeName)) { attributeName = null; continue; } attributeName = null; // If it's an unwanted element, don't spell check its XML text if (elementName != null && !name.Contains("attribute value") && ignoredXmlElements.Contains(elementName)) { continue; } // Include files in C/C++ are tagged as a string but we don't want to spell check them if (preprocessorKeywordSeen && name == "string" && classificationSpan.Span.Snapshot.ContentType.IsOfType("C/C++")) { continue; } preprocessorKeywordSeen = false; // Track and ignore classifications by original name to allow the user to be more // selective if necessary. classificationCache.Add(originalName); if (ignoredClassifications.Contains(originalName)) { continue; } yield return(new TagSpan <NaturalTextTag>(classificationSpan.Span, new NaturalTextTag())); } else if (name == "preprocessor keyword") { preprocessorKeywordSeen = true; } } } }
//===================================================================== /// <inheritdoc /> public IEnumerable <ITagSpan <NaturalTextTag> > GetTags(NormalizedSnapshotSpanCollection spans) { List <SnapshotSpan> ignoredSpans = new List <SnapshotSpan>(); string text; int start, end; if (classifier == null || spans == null || spans.Count == 0) { yield break; } ITextSnapshot snapshot = spans[0].Snapshot; foreach (var snapshotSpan in spans) { Debug.Assert(snapshotSpan.Snapshot.TextBuffer == buffer); ignoredSpans.Clear(); // The classifier for this one doesn't return natural language spans so we'll get those below. // First, get a list of stuff we can handle directly and/or ignore below. foreach (ClassificationSpan classificationSpan in classifier.GetClassificationSpans(snapshotSpan)) { string name = classificationSpan.ClassificationType.Classification.ToLowerInvariant(); switch (name) { case "markdown monospace": // Typically code keywords, etc. case "keyword": // RDoc stuff case "number": case "punctuation": case "rd braces": ignoredSpans.Add(classificationSpan.Span); break; case "markdown italic text": // Italics may be denoted with underscores so we'll need to trim them off of the // span or it will not spell check the text if the "treat underscore as separator" // option is turned off. text = classificationSpan.Span.GetText(); start = 0; while (start < text.Length && text[start] == '_') { start++; } end = text.Length - 1; while (end > start && text[end] == '_') { end--; } end++; if (end - start > 1) { SnapshotSpan s = new SnapshotSpan(classificationSpan.Span.Start + start, end - start); ignoredSpans.Add(s); classificationCache.Add(name); if (!ignoredClassifications.Contains(name)) { yield return(new TagSpan <NaturalTextTag>(s, new NaturalTextTag())); } } break; default: classificationCache.Add(name); if (ignoredClassifications.Contains(name)) { ignoredSpans.Add(classificationSpan.Span); } break; } } // Now return the spans we didn't ignore or handle above start = snapshotSpan.Start; foreach (var ignored in ignoredSpans.OrderBy(s => s.Start)) { if (ignored.Start > start) { yield return(new TagSpan <NaturalTextTag>(new SnapshotSpan(snapshotSpan.Snapshot, start, ignored.Start.Position - start), new NaturalTextTag())); } start = ignored.Start + ignored.Length; } if (start < snapshotSpan.End) { yield return(new TagSpan <NaturalTextTag>(new SnapshotSpan(snapshotSpan.Snapshot, start, snapshotSpan.End.Position - start), new NaturalTextTag())); } } }