///////////////////////////////////////////////////////////////////////////////////////////////////// // NON-PUBLIC PROCEDURES ///////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Rescan the document and look for instances of the word 'Actipro' to mark with squiggle tags. /// </summary> private void RefreshSquiggleTags() { // Get the tagger that was created by the language and has been persisted in the document's properties // while the language is active on the document CustomSquiggleTagger tagger = null; if (editor.Document.Properties.TryGetValue(typeof(CustomSquiggleTagger), out tagger)) { using (var batch = tagger.CreateBatch()) { // Clear existing tags tagger.Clear(); // In this example we are going to construct the full snapshot text string... this is not generally a good // idea for a production application since doing so for a large document in the UI thread can negatively affect performance... // But this example shows how any text range results (such as those from an external error scan) can be used to generate squiggle tags var snapshot = editor.ActiveView.CurrentSnapshot; var snapshotText = snapshot.GetText(LineTerminator.Newline); // Look for regex pattern matches var matches = Regex.Matches(snapshotText, @"\bActipro\b", RegexOptions.IgnoreCase); for (var matchIndex = 0; matchIndex < matches.Count; matchIndex++) { var match = matches[matchIndex]; // Create a version range for the match var snapshotRange = new TextSnapshotRange(snapshot, TextRange.FromSpan(match.Index, match.Length)); var versionRange = snapshotRange.ToVersionRange(TextRangeTrackingModes.DeleteWhenZeroLength); // Create a tag, and include a quick info tip if specified var tag = new SquiggleTag(); tag.ClassificationType = ClassificationTypes.Warning; // This classification type is mapped in the tagger to a Green color tag.ContentProvider = new PlainTextContentProvider(String.Format("Instance number {0}", matchIndex + 1)); // Add the tag to the tagger tagger.Add(new TagVersionRange <ISquiggleTag>(versionRange, tag)); } } } }
///////////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC PROCEDURES ///////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Returns the tag ranges that intersect with the specified normalized snapshot ranges. /// </summary> /// <param name="snapshotRanges">The collection of normalized snapshot ranges.</param> /// <param name="parameter">An optional parameter that provides contextual information about the tag request.</param> /// <returns>The tag ranges that intersect with the specified normalized snapshot ranges.</returns> public override IEnumerable <TagSnapshotRange <IClassificationTag> > GetTags(NormalizedTextSnapshotRangeCollection snapshotRanges, object parameter) { // Loop through the requested snapshot ranges... foreach (TextSnapshotRange snapshotRange in snapshotRanges) { // If the snapshot range is not zero-length... if (!snapshotRange.IsZeroLength) { IEnumerable <TagSnapshotRange <ITokenTag> > tokenTagRanges = tokenTagAggregator.GetTags(snapshotRange); if (tokenTagRanges != null) { foreach (TagSnapshotRange <ITokenTag> tokenTagRange in tokenTagRanges) { if (tokenTagRange.Tag.Token != null) { switch (tokenTagRange.Tag.Token.Key) { case "XmlCommentText": { if (highlightDocumentationComments) { // Get the text of the token string text = tokenTagRange.SnapshotRange.Text; // Look for the text "Actipro" int index = text.IndexOf("Actipro"); while (index != -1) { // Add a highlighted range yield return(new TagSnapshotRange <IClassificationTag>( new TextSnapshotRange(snapshotRange.Snapshot, TextRange.FromSpan(tokenTagRange.SnapshotRange.StartOffset + index, 7)), new ClassificationTag(ClassificationTypes.SyntaxError) )); // Look for another match index = text.IndexOf("Actipro", index + 7); } } break; } case "Identifier": { if (highlightIdentifiers) { // Get the text of the token string text = tokenTagRange.SnapshotRange.Text; // If the text is "Actipro"... if (text == "Actipro") { // Add a highlighted range yield return(new TagSnapshotRange <IClassificationTag>( new TextSnapshotRange(snapshotRange.Snapshot, tokenTagRange.SnapshotRange.TextRange), new ClassificationTag(ClassificationTypes.SyntaxError) )); } } break; } } } } } } } }
///////////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC PROCEDURES ///////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Performs a parsing operation using the parameters specified in the supplied <see cref="IParseRequest"/> /// and returns the resulting parse data. /// </summary> /// <param name="request">The <see cref="IParseRequest"/> that contains data about the requested parsing operation.</param> /// <returns>An <see cref="IParseData"/> that is the result of the parsing operation.</returns> /// <remarks> /// A <see cref="IParseRequestDispatcher"/> typically calls this method when a queued parse request is ready to be processed. /// </remarks> public override IParseData Parse(IParseRequest request) { if (request == null) { throw new ArgumentNullException("request"); } // Create parse data ParentParseData parseData = new ParentParseData(); parseData.Snapshot = request.Snapshot; // Initialize generated text StringBuilder generatedText = new StringBuilder(); generatedText.Append("using System;\n"); generatedText.Append("using System.Collections.Generic;\n\n"); generatedText.Append("using System.Linq;\n\n"); generatedText.Append("[EditorBrowsable(EditorBrowsableState.Never)]\n"); generatedText.Append("class __Generated {\n"); generatedText.Append("\t[EditorBrowsable(EditorBrowsableState.Never)]\n"); generatedText.Append("\tvoid __WriteOutput() {\n"); ITextSnapshotReader sourceReader = request.Snapshot.GetReader(0); int lastDelimiterOffset = 0; bool lastDelimiterWasStart = false; while (!sourceReader.IsAtSnapshotEnd) { IToken token = sourceReader.ReadToken(); if (token != null) { switch (token.Id) { case ParentTokenId.ChildCodeBlockStart: case ParentTokenId.ChildOutputBlockStart: if (token.StartOffset - lastDelimiterOffset > 0) { // Append generated text string text = sourceReader.Snapshot.GetSubstring(new TextRange(lastDelimiterOffset, token.StartOffset), LineTerminator.Newline); generatedText.Append("\t\tResponse.Write(@\""); generatedText.Append(text.Replace("\"", "\"\"")); generatedText.Append("\");\n"); } // Store the last delimiter offset lastDelimiterOffset = token.EndOffset; lastDelimiterWasStart = true; break; case ParentTokenId.ChildCodeBlockEnd: if ((lastDelimiterWasStart) && (token.StartOffset - lastDelimiterOffset > 0)) { // Get the text between the delimiters string text = sourceReader.Snapshot.GetSubstring(new TextRange(lastDelimiterOffset, token.StartOffset), LineTerminator.Newline); generatedText.Append("\t\t"); // Add a mapping parseData.TextRangeMappings.Add(Tuple.Create(new TextRange(lastDelimiterOffset, token.StartOffset), TextRange.FromSpan(generatedText.Length, text.Length))); // Append the text directly generatedText.Append(text); generatedText.Append("\n"); } // Store the last delimiter offset lastDelimiterOffset = token.EndOffset; lastDelimiterWasStart = false; break; case ParentTokenId.ChildOutputBlockEnd: if ((lastDelimiterWasStart) && (token.StartOffset - lastDelimiterOffset > 0)) { // Get the text between the delimiters and append a Response.Write string text = sourceReader.Snapshot.GetSubstring(new TextRange(lastDelimiterOffset, token.StartOffset), LineTerminator.Newline); generatedText.Append("\t\tResponse.Write("); // Add a mapping parseData.TextRangeMappings.Add(Tuple.Create(new TextRange(lastDelimiterOffset, token.StartOffset), TextRange.FromSpan(generatedText.Length, text.Length))); // Append the text directly generatedText.Append(text); generatedText.Append(");\n"); } // Store the last delimiter offset lastDelimiterOffset = token.EndOffset; lastDelimiterWasStart = false; break; } } } if (lastDelimiterOffset < sourceReader.Snapshot.Length) { // Append generated text string text = sourceReader.Snapshot.GetSubstring(new TextRange(lastDelimiterOffset, sourceReader.Snapshot.Length), LineTerminator.Newline); generatedText.Append("\t\tResponse.Write(@\""); generatedText.Append(text.Replace("\"", "\"\"")); generatedText.Append("\");\n"); } // Store the generated text generatedText.Append("\t}\n"); generatedText.Append("}\n"); // Get parse data for the translated code CodeDocument generatedDocument = new CodeDocument(); generatedDocument.Language = childLanguage; generatedDocument.SetText(generatedText.ToString()); // Get a reader ITextBufferReader generatedReader = generatedDocument.CurrentSnapshot.GetReader(0).BufferReader; // Create a request ParseRequest generatedRequest = new ParseRequest(Guid.NewGuid().ToString(), generatedReader, childParser, generatedDocument); generatedRequest.Snapshot = generatedDocument.CurrentSnapshot; // Parse generatedDocument.ParseData = childParser.Parse(generatedRequest); parseData.GeneratedParseData = generatedDocument.ParseData as ILLParseData; return(parseData); }
///////////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC PROCEDURES ///////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Returns the tag ranges that intersect with the specified normalized snapshot ranges. /// </summary> /// <param name="snapshotRanges">The collection of normalized snapshot ranges.</param> /// <param name="parameter">An optional parameter that provides contextual information about the tag request.</param> /// <returns>The tag ranges that intersect with the specified normalized snapshot ranges.</returns> public override IEnumerable <TagSnapshotRange <IClassificationTag> > GetTags(NormalizedTextSnapshotRangeCollection snapshotRanges, object parameter) { // Get a regex of the current word var search = new Regex(Regex.Escape(HighlightedString), RegexOptions.Singleline | RegexOptions.IgnoreCase); // Loop through the requested snapshot ranges... foreach (TextSnapshotRange snapshotRange in snapshotRanges) { // If the snapshot range is not zero-length... if (!snapshotRange.IsZeroLength) { // Look for current word matches foreach (Match match in search.Matches(snapshotRange.Text)) { // Add a highlighted range yield return(new TagSnapshotRange <IClassificationTag>( new TextSnapshotRange(snapshotRange.Snapshot, TextRange.FromSpan(snapshotRange.StartOffset + match.Index, match.Length)), new ClassificationTag(wordHighlightClassificationType) )); } } } }