/// <summary> /// Examines the AST data to determine the context type and insert the containing function declaration. /// </summary> /// <param name="context">The <see cref="SimpleContext"/> to update.</param> private static void UpdateFromAst(SimpleContext context) { // Get the snapshot offset TextSnapshotOffset snapshotOffset = context.SnapshotOffset; // Get the document ICodeDocument document = snapshotOffset.Snapshot.Document as ICodeDocument; if (document == null) { return; } ILLParseData parseData = document.ParseData as ILLParseData; if (parseData != null) { CompilationUnit compilationUnit = parseData.Ast as CompilationUnit; if ((compilationUnit != null) && (compilationUnit.HasMembers)) { // Translate the snapshot offset to the AST's snapshot if (parseData.Snapshot != null) { snapshotOffset = snapshotOffset.TranslateTo(parseData.Snapshot, TextOffsetTrackingMode.Negative); } // Loop through AST nodes foreach (FunctionDeclaration functionAstNode in compilationUnit.Members) { // If the child node is a function declaration with valid offsets... if ((functionAstNode.StartOffset.HasValue) && (functionAstNode.EndOffset.HasValue)) { // If the function's text range contains the offset... TextRange functionTextRange = new TextRange(functionAstNode.StartOffset.Value, functionAstNode.EndOffset.Value); if (functionTextRange.Contains(snapshotOffset.Offset)) { // Initially assume we are in a header context.Type = SimpleContextType.FunctionDeclarationHeader; context.ContainingFunctionDeclaration = functionAstNode; // If the function has a body with a range... if ((functionAstNode.Body != null) && (functionAstNode.Body.StartOffset.HasValue) && (functionAstNode.Body.EndOffset.HasValue)) { // If the block's text range contains the offset... TextRange blockTextRange = new TextRange(functionAstNode.Body.StartOffset.Value + 1, functionAstNode.Body.EndOffset.Value - 1); if (blockTextRange.Contains(snapshotOffset.Offset)) { // Mark that we are in a block instead context.Type = SimpleContextType.FunctionDeclarationBlock; } } break; } } } } } }
///////////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC PROCEDURES ///////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Creates a <see cref="SimpleContext"/> for the specified <see cref="TextSnapshotOffset"/>. /// </summary> /// <param name="snapshotOffset">The <see cref="TextSnapshotOffset"/> for which to create a context.</param> /// <param name="includeArgumentInfo">Whether to populate the argument-related context properties, for use with parameter info.</param> /// <returns>The <see cref="SimpleContext"/> that was created.</returns> public SimpleContext CreateContext(TextSnapshotOffset snapshotOffset, bool includeArgumentInfo) { // Create a context SimpleContext context = new SimpleContext(snapshotOffset); // Update the context from the AST UpdateFromAst(context); // Update the context from the snapshot text UpdateFromSnapshotText(context, includeArgumentInfo); return(context); }
/// <summary> /// Determines whether the specified <see cref="Object"/> is equal to the current <see cref="Object"/>. /// </summary> /// <param name="obj">The <see cref="Object"/> to compare with the current <see cref="Object"/>.</param> /// <returns> /// true if the specified <see cref="Object"/> is equal to the current <see cref="Object"/>; otherwise, false. /// </returns> /// <exception cref="NullReferenceException">The <paramref name="obj"/> parameter is null.</exception> public override bool Equals(object obj) { SimpleContext context = obj as SimpleContext; if (context != null) { // Used for IntelliPrompt quick info bool similar = (context.InitializationSnapshotRange == this.InitializationSnapshotRange) && (context.Type == this.Type); return(similar); } else { return(false); } }
/// <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(); } } } } }