///////////////////////////////////////////////////////////////////////////////////////////////////// // 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) { SimpleParseData parseData = new SimpleParseData(); // // NOTE: Make sure that you've set up an ambient parse request dispatcher for your application // (see documentation on 'Parse Requests and Dispatchers') so that this parser is called in // a worker thread as the editor is updated // // Most parsers will use the request.TextBufferReader property in some fashion to scan through // text and not a snapshot directly... in this basic sample though, we're going to use the // tokenization provided by the snapshot's reader so we can only proceed if there is a // snapshot passed to us if (request.Snapshot != null) { ITextSnapshotReader reader = request.Snapshot.GetReader(0); bool isFunctionStart = false; while (!reader.IsAtSnapshotEnd) { IToken token = reader.ReadToken(); if (token != null) { switch (token.Key) { case "Keyword": // If a function token, mark that this is a function start... the next identifier should be the function name isFunctionStart = (reader.Snapshot.GetSubstring(token.TextRange) == "function"); break; case "Identifier": // If this is the function name... if (isFunctionStart) { parseData.Functions.Add(new TextSnapshotRange(reader.Snapshot, token.TextRange)); isFunctionStart = false; } break; case "Whitespace": // Ignore break; default: // Flag as no longer in a function start isFunctionStart = false; break; } } } } return(parseData); }
///////////////////////////////////////////////////////////////////////////////////////////////////// // 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); }
/// <summary> /// Initializes a new instance of the <c>JavascriptOutliningSource</c> class. /// </summary> /// <param name="snapshot">The <see cref="ITextSnapshot"/> to use for this outlining source.</param> public JavascriptOutliningSource(ITextSnapshot snapshot) : base(snapshot) { int curlyBraceStartOffset = -1; int curlyBraceLevel = 0; int commentStartOffset = -1; IToken token; // Get a text snapshot reader so that we can read tokens ITextSnapshotReader reader = snapshot.GetReader(0); // Read through the entire snapshot while (!reader.IsAtSnapshotEnd) { // Get the next token token = reader.ReadToken(); if (token != null) { switch (token.Key) { case "MultiLineCommentStartDelimiter": // A multi-line comment is starting... save its start offset if (commentStartOffset == -1) { commentStartOffset = token.StartOffset; } break; case "MultiLineCommentEndDelimiter": // A multi-line comment is ending... add its range to the outlining source if (commentStartOffset != -1) { this.AddNode(new TextRange(commentStartOffset, token.EndOffset), multiLineCommentDefinition); commentStartOffset = -1; } break; case "OpenCurlyBrace": // An open curly brace... save its start offset if it's a top-level brace if (curlyBraceLevel++ == 0) { if (curlyBraceStartOffset == -1) { curlyBraceStartOffset = token.StartOffset; } } break; case "CloseCurlyBrace": // A close curly brace... add its range to the outlining source if it's a top-level brace if (curlyBraceLevel > 0) { curlyBraceLevel--; if (curlyBraceLevel == 0) { this.AddNode(new TextRange(curlyBraceStartOffset, token.EndOffset), curlyBraceDefinition); curlyBraceStartOffset = -1; } } break; } } else { break; } } // If there are any "open" nodes (never found a matching end), add them too if (commentStartOffset != -1) { this.AddOpenNode(commentStartOffset, multiLineCommentDefinition); } if (curlyBraceStartOffset != -1) { this.AddOpenNode(curlyBraceStartOffset, curlyBraceDefinition); } }
/// <summary> /// Examines the snapshot text to determine more detail about the context. /// </summary> /// <param name="context">The <see cref="SimpleContext"/> to update.</param> /// <param name="includeArgumentInfo">Whether to populate the argument-related context properties, for use with parameter info.</param> private static void UpdateFromSnapshotText(SimpleContext context, bool includeArgumentInfo) { // Get the snapshot offset TextSnapshotOffset snapshotOffset = context.SnapshotOffset; // Create a default initialization range context.InitializationSnapshotRange = new TextSnapshotRange(snapshotOffset); // Get a snapshot reader ITextSnapshotReader reader = snapshotOffset.Snapshot.GetReader(snapshotOffset.Offset); if (reader == null) { return; } IToken token = reader.ReadToken(); if (token != null) { // If the current token is a comment, flag no context switch (token.Id) { case SimpleTokenId.MultiLineCommentEndDelimiter: case SimpleTokenId.MultiLineCommentLineTerminator: case SimpleTokenId.MultiLineCommentStartDelimiter: case SimpleTokenId.MultiLineCommentText: case SimpleTokenId.SingleLineCommentEndDelimiter: case SimpleTokenId.SingleLineCommentStartDelimiter: case SimpleTokenId.SingleLineCommentText: context.Type = SimpleContextType.None; return; } } // If we are in a function declaration block... if (context.Type == SimpleContextType.FunctionDeclarationBlock) { // Get the AST ICodeDocument document = snapshotOffset.Snapshot.Document as ICodeDocument; if (document == null) { return; } ILLParseData parseData = document.ParseData as ILLParseData; if (parseData == null) { return; } CompilationUnit compilationUnit = parseData.Ast as CompilationUnit; if ((compilationUnit == null) || (!compilationUnit.HasMembers)) { return; } // If argument data should be scanned... if (includeArgumentInfo) { // Scan backward to look for argument data reader.Offset = snapshotOffset.Offset; reader.GoToCurrentTokenStart(); var startOffset = reader.Offset; token = reader.ReadTokenReverse(); while (token != null) { // Quit if we look behind too far (for performance reasons) - 500 is arbitrary number and could be tweaked if ((!context.ArgumentIndex.HasValue) && (startOffset - reader.Offset > 500)) { return; } switch (token.Id) { case SimpleTokenId.CloseParenthesis: // Skip over ( ... ) pair if (!reader.GoToPreviousMatchingTokenById(SimpleTokenId.CloseParenthesis, SimpleTokenId.OpenParenthesis)) { return; } break; case SimpleTokenId.Comma: // Update the context data if (context.ArgumentIndex.HasValue) { context.ArgumentIndex++; } else { context.ArgumentIndex = 1; context.ArgumentSnapshotOffset = new TextSnapshotOffset(reader.Snapshot, token.EndOffset); } break; case SimpleTokenId.OpenParenthesis: // Update the context data context.ArgumentListSnapshotOffset = new TextSnapshotOffset(reader.Snapshot, token.EndOffset); if (!context.ArgumentIndex.HasValue) { context.ArgumentIndex = 0; context.ArgumentSnapshotOffset = context.ArgumentListSnapshotOffset; } // Go to the previous token reader.GoToPreviousToken(); token = reader.Token; if ((token != null) && (token.Id == SimpleTokenId.Identifier)) { // Loop through the AST nodes to ensure that the identifier text matches a declared function name string functionName = reader.TokenText; foreach (FunctionDeclaration functionAstNode in compilationUnit.Members) { // If the name matches the function's name... if (functionAstNode.Name == functionName) { // Update the target function context.TargetFunction = functionAstNode; break; } } } return; default: // Quit if any tokens are found that aren't allowed in invocations if (!IsTokenAllowedInInvocation(token.Id)) { return; } else { break; } } // Move back a token token = reader.ReadTokenReverse(); } } else { // If the current token is an identifier... if ((token != null) && (token.Id == SimpleTokenId.Identifier)) { // Store the identifier snapshot range in case this is a function reference token TextSnapshotRange identifierSnapshotRange = new TextSnapshotRange(reader.Snapshot, token.TextRange); // Next, check to ensure the next non-whitespace token is a '(' token = reader.ReadToken(); while (!reader.IsAtSnapshotEnd) { if (token != null) { switch (token.Id) { case SimpleTokenId.Whitespace: // Continue break; case SimpleTokenId.OpenParenthesis: { // Loop through the AST nodes to ensure that the identifier text matches a declared function name foreach (FunctionDeclaration functionAstNode in compilationUnit.Members) { // If the name matches the function's name... if (functionAstNode.Name == identifierSnapshotRange.Text) { // Update the context type context.Type = SimpleContextType.FunctionReference; context.InitializationSnapshotRange = identifierSnapshotRange; context.TargetFunction = functionAstNode; break; } } return; } default: // Quit return; } } // Advance token = reader.ReadToken(); } } } } }