diff --git a/Source/Lib/CompilerServices/CSharp/BinderCase/CSharpBinder.Expressions.cs b/Source/Lib/CompilerServices/CSharp/BinderCase/CSharpBinder.Expressions.cs index 1bf4b6cca..e5ac6ce72 100644 --- a/Source/Lib/CompilerServices/CSharp/BinderCase/CSharpBinder.Expressions.cs +++ b/Source/Lib/CompilerServices/CSharp/BinderCase/CSharpBinder.Expressions.cs @@ -145,6 +145,37 @@ public IExpressionNode AnyMergeExpression( public IExpressionNode HandleBinaryOperator( IExpressionNode expressionPrimary, ISyntaxToken token, CSharpCompilationUnit compilationUnit, ref CSharpParserModel parserModel) { + // In order to disambiguate '<' between when the 'expressionPrimary' is an 'AmbiguousIdentifierExpressionNode' + // - Less than operator + // - GenericParametersListingNode + // + // Invoke 'ForceDecisionAmbiguousIdentifier(...)' to determine what the true SyntaxKind is. + // + // If its true SyntaxKind is a TypeClauseNode, then parse the '<' + // to be the start of a 'GenericParametersListingNode'. + // + // Otherwise presume that the '<' is the 'less than operator'. + if (expressionPrimary.SyntaxKind == SyntaxKind.AmbiguousIdentifierExpressionNode) + { + expressionPrimary = ForceDecisionAmbiguousIdentifier( + EmptyExpressionNode.Empty, + (AmbiguousIdentifierExpressionNode)expressionPrimary, + compilationUnit, + ref parserModel); + } + + if (expressionPrimary.SyntaxKind == SyntaxKind.TypeClauseNode && + (token.SyntaxKind == SyntaxKind.OpenAngleBracketToken || token.SyntaxKind == SyntaxKind.CloseAngleBracketToken)) + { + return TypeClauseMergeToken((TypeClauseNode)expressionPrimary, token, compilationUnit, ref parserModel); + } + + /*if (expressionPrimary.SyntaxKind == SyntaxKind.VariableReferenceNode && + token.SyntaxKind == SyntaxKind.EqualsToken) + { + return VariableA + }*/ + var parentExpressionNode = GetParentNode(expressionPrimary, compilationUnit, ref parserModel); if (parentExpressionNode.SyntaxKind == SyntaxKind.BinaryExpressionNode) @@ -243,22 +274,16 @@ public IExpressionNode AmbiguousIdentifierMergeToken( functionInvocationNode, compilationUnit); - return ParseFunctionParametersListingNode(functionInvocationNode, functionInvocationNode.FunctionParametersListingNode, compilationUnit, ref parserModel); + return ParseFunctionParametersListingNode( + functionInvocationNode, functionInvocationNode.FunctionParametersListingNode, compilationUnit, ref parserModel); } else if (token.SyntaxKind == SyntaxKind.OpenAngleBracketToken) { - if (ambiguousIdentifierExpressionNode.GenericParametersListingNode is null) - { - ambiguousIdentifierExpressionNode.SetGenericParametersListingNode( - new GenericParametersListingNode( - (OpenAngleBracketToken)token, - new List(), - closeAngleBracketToken: default)); - } - - parserModel.ExpressionList.Add((SyntaxKind.CloseAngleBracketToken, ambiguousIdentifierExpressionNode)); - parserModel.ExpressionList.Add((SyntaxKind.CommaToken, ambiguousIdentifierExpressionNode.GenericParametersListingNode)); - return EmptyExpressionNode.Empty; + // TODO: As of (2024-12-21) is this conditional branch no longer hit?... + // ...The 'AmbiguousIdentifierExpressionNode' merging with 'OpenAngleBracketToken' + // code was moved to 'HandleBinaryOperator(...)' + return ParseAmbiguousIdentifierGenericParametersListingNode( + ambiguousIdentifierExpressionNode, (OpenAngleBracketToken)token, compilationUnit, ref parserModel); } else if (token.SyntaxKind == SyntaxKind.CloseAngleBracketToken) { @@ -270,13 +295,6 @@ public IExpressionNode AmbiguousIdentifierMergeToken( } else if (token.SyntaxKind == SyntaxKind.EqualsToken) { - if (parserModel.TokenWalker.Next.SyntaxKind == SyntaxKind.CloseAngleBracketToken) - { - var lambdaExpressionNode = new LambdaExpressionNode(CSharpFacts.Types.Void.ToTypeClause()); - SetLambdaExpressionNodeVariableDeclarationNodeList(lambdaExpressionNode, ambiguousIdentifierExpressionNode, compilationUnit, ref parserModel); - return lambdaExpressionNode; - } - // Thinking about: Variable Assignment, Optional Parameters, and other unknowns. if (UtilityApi.IsConvertibleToIdentifierToken(ambiguousIdentifierExpressionNode.Token.SyntaxKind)) { @@ -296,6 +314,38 @@ public IExpressionNode AmbiguousIdentifierMergeToken( return EmptyExpressionNode.Empty; } } + else if (token.SyntaxKind == SyntaxKind.EqualsCloseAngleBracketToken) + { + var lambdaExpressionNode = new LambdaExpressionNode(CSharpFacts.Types.Void.ToTypeClause()); + SetLambdaExpressionNodeVariableDeclarationNodeList(lambdaExpressionNode, ambiguousIdentifierExpressionNode, compilationUnit, ref parserModel); + + // If the lambda expression's code block is a single expression then there is no end delimiter. + // Instead, it is the parent expression's delimiter that causes the lambda expression's code block to short circuit. + // At this moment, the lambda expression is given whatever expression was able to be parsed and can take it as its "code block". + // And then restore the parent expression as the expressionPrimary. + // + // ----------------------------------------------------------------------------------------------------------------------------- + // + // If the lambda expression's code block is deliminated by braces + // then the end delimiter is the CloseBraceToken. + // But, we can only add a "short circuit" for 'CloseBraceToken and lambdaExpressionNode' + // if we have seen the 'OpenBraceToken'. + + parserModel.ExpressionList.Add((SyntaxKind.EndOfFileToken, lambdaExpressionNode)); + + if (parserModel.TokenWalker.Next.SyntaxKind == SyntaxKind.OpenBraceToken) + { + parserModel.ExpressionList.Add((SyntaxKind.CloseBraceToken, lambdaExpressionNode)); + OpenLambdaExpressionScope(lambdaExpressionNode, (OpenBraceToken)parserModel.TokenWalker.Next, compilationUnit, ref parserModel); + SkipLambdaExpressionStatements(lambdaExpressionNode, compilationUnit, ref parserModel); + } + else + { + OpenLambdaExpressionScope(lambdaExpressionNode, new OpenBraceToken(token.TextSpan), compilationUnit, ref parserModel); + } + + return EmptyExpressionNode.Empty; + } else if (token.SyntaxKind == SyntaxKind.IsTokenKeyword) { ForceDecisionAmbiguousIdentifier( @@ -313,15 +363,8 @@ public IExpressionNode AmbiguousIdentifierMergeToken( { _ = parserModel.TokenWalker.Consume(); // Consume the NullTokenKeyword } - else if (UtilityApi.IsConvertibleToIdentifierToken(parserModel.TokenWalker.Current.SyntaxKind)) - { - ParseTokens.ParseIdentifierToken(compilationUnit, ref parserModel); // Parse the type pattern matching / variable declaration - } - - // Guaranteed to consume 1 further than the secondary loop so have to backtrack 1 time as well. - _ = parserModel.TokenWalker.Backtrack(); - return ambiguousIdentifierExpressionNode; + return EmptyExpressionNode.Empty; } else if (token.SyntaxKind == SyntaxKind.WithTokenContextualKeyword) { @@ -425,7 +468,10 @@ public IExpressionNode ForceDecisionAmbiguousIdentifier( bool forceVariableReferenceNode = false) { if (ambiguousIdentifierExpressionNode.FollowsMemberAccessToken) - return ambiguousIdentifierExpressionNode; + { + if (parserModel.TokenWalker.Current.SyntaxKind != SyntaxKind.OpenAngleBracketToken) + return ambiguousIdentifierExpressionNode; + } if (UtilityApi.IsConvertibleToIdentifierToken(ambiguousIdentifierExpressionNode.Token.SyntaxKind)) { @@ -462,6 +508,22 @@ public IExpressionNode ForceDecisionAmbiguousIdentifier( } } + if (ambiguousIdentifierExpressionNode.Token.SyntaxKind == SyntaxKind.IdentifierToken && + ambiguousIdentifierExpressionNode.Token.TextSpan.Length == 1 && + ambiguousIdentifierExpressionNode.Token.TextSpan.GetText() == "_") + { + if (!compilationUnit.Binder.TryGetVariableDeclarationHierarchically( + compilationUnit, + compilationUnit.BinderSession.ResourceUri, + compilationUnit.BinderSession.CurrentScopeIndexKey, + ambiguousIdentifierExpressionNode.Token.TextSpan.GetText(), + out _)) + { + compilationUnit.Binder.BindDiscard((IdentifierToken)ambiguousIdentifierExpressionNode.Token, compilationUnit); + return ambiguousIdentifierExpressionNode; + } + } + // Bind an undefined-TypeClauseNode if (!forceVariableReferenceNode || UtilityApi.IsConvertibleToTypeClauseNode(ambiguousIdentifierExpressionNode.Token.SyntaxKind)) @@ -558,6 +620,15 @@ public IExpressionNode BinaryMergeExpression( { if (binaryExpressionNode.RightExpressionNode.SyntaxKind == SyntaxKind.EmptyExpressionNode) { + if (expressionSecondary.SyntaxKind == SyntaxKind.AmbiguousIdentifierExpressionNode) + { + expressionSecondary = ForceDecisionAmbiguousIdentifier( + EmptyExpressionNode.Empty, + (AmbiguousIdentifierExpressionNode)expressionSecondary, + compilationUnit, + ref parserModel); + } + binaryExpressionNode.SetRightExpressionNode(expressionSecondary); return binaryExpressionNode; } @@ -914,12 +985,6 @@ public IExpressionNode EmptyMergeToken( BindStringVerbatimExpression((AtToken)token, compilationUnit); return emptyExpressionNode; case SyntaxKind.OutTokenKeyword: - if (UtilityApi.IsConvertibleToIdentifierToken(parserModel.TokenWalker.Current.SyntaxKind)) - { - // Parse the variable reference / variable declaration - ParseTokens.ParseIdentifierToken(compilationUnit, ref parserModel); - } - return emptyExpressionNode; case SyntaxKind.InTokenKeyword: case SyntaxKind.RefTokenKeyword: @@ -1155,20 +1220,11 @@ public IExpressionNode ReturnStatementMergeExpression( public IExpressionNode LambdaMergeToken( LambdaExpressionNode lambdaExpressionNode, ISyntaxToken token, CSharpCompilationUnit compilationUnit, ref CSharpParserModel parserModel) { - if (token.SyntaxKind == SyntaxKind.CloseAngleBracketToken) + if (token.SyntaxKind == SyntaxKind.EqualsCloseAngleBracketToken) { - int startInclusiveIndex; - - if (parserModel.TokenWalker.Previous.SyntaxKind == SyntaxKind.EqualsToken) - startInclusiveIndex = parserModel.TokenWalker.Previous.TextSpan.StartingIndexInclusive; - else - startInclusiveIndex = token.TextSpan.StartingIndexInclusive; - - var endExclusiveIndex = token.TextSpan.EndingIndexExclusive; - var textSpan = new TextEditorTextSpan( - startInclusiveIndex, - endExclusiveIndex, + token.TextSpan.StartingIndexInclusive, + token.TextSpan.EndingIndexExclusive, (byte)GenericDecorationKind.None, token.TextSpan.ResourceUri, token.TextSpan.SourceText); @@ -1265,6 +1321,9 @@ public IExpressionNode LambdaMergeExpression( switch (expressionSecondary.SyntaxKind) { default: + if (!lambdaExpressionNode.CloseBraceToken.ConstructorWasInvoked) + CloseLambdaExpressionScope(lambdaExpressionNode, compilationUnit, ref parserModel); + return lambdaExpressionNode; } } @@ -1297,15 +1356,10 @@ public IExpressionNode ParenthesizedMergeToken( } return parenthesizedExpressionNode.SetCloseParenthesisToken((CloseParenthesisToken)token); - case SyntaxKind.EqualsToken: - if (parserModel.TokenWalker.Next.SyntaxKind == SyntaxKind.CloseAngleBracketToken) - { - var lambdaExpressionNode = new LambdaExpressionNode(CSharpFacts.Types.Void.ToTypeClause()); - SetLambdaExpressionNodeVariableDeclarationNodeList(lambdaExpressionNode, parenthesizedExpressionNode.InnerExpression, compilationUnit, ref parserModel); - return lambdaExpressionNode; - } - - return parenthesizedExpressionNode; + case SyntaxKind.EqualsCloseAngleBracketToken: + var lambdaExpressionNode = new LambdaExpressionNode(CSharpFacts.Types.Void.ToTypeClause()); + SetLambdaExpressionNodeVariableDeclarationNodeList(lambdaExpressionNode, parenthesizedExpressionNode.InnerExpression, compilationUnit, ref parserModel); + return lambdaExpressionNode; default: return new BadExpressionNode(CSharpFacts.Types.Void.ToTypeClause(), parenthesizedExpressionNode, token); } @@ -1314,8 +1368,7 @@ public IExpressionNode ParenthesizedMergeToken( public IExpressionNode ParenthesizedMergeExpression( ParenthesizedExpressionNode parenthesizedExpressionNode, IExpressionNode expressionSecondary, CSharpCompilationUnit compilationUnit, ref CSharpParserModel parserModel) { - if (parserModel.TokenWalker.Peek(1).SyntaxKind == SyntaxKind.EqualsToken && - parserModel.TokenWalker.Peek(2).SyntaxKind == SyntaxKind.CloseAngleBracketToken) + if (parserModel.TokenWalker.Next.SyntaxKind == SyntaxKind.EqualsCloseAngleBracketToken) { var lambdaExpressionNode = new LambdaExpressionNode(CSharpFacts.Types.Void.ToTypeClause()); return SetLambdaExpressionNodeVariableDeclarationNodeList(lambdaExpressionNode, expressionSecondary, compilationUnit, ref parserModel); @@ -1364,18 +1417,8 @@ public IExpressionNode TypeClauseMergeToken( switch (token.SyntaxKind) { case SyntaxKind.OpenAngleBracketToken: - if (typeClauseNode.GenericParametersListingNode is null) - { - typeClauseNode.SetGenericParametersListingNode( - new GenericParametersListingNode( - (OpenAngleBracketToken)token, - new List(), - closeAngleBracketToken: default)); - } - - parserModel.ExpressionList.Add((SyntaxKind.CloseAngleBracketToken, typeClauseNode)); - parserModel.ExpressionList.Add((SyntaxKind.CommaToken, typeClauseNode.GenericParametersListingNode)); - return EmptyExpressionNode.Empty; + return ParseTypeClauseNodeGenericParametersListingNode( + typeClauseNode, (OpenAngleBracketToken)token, compilationUnit, ref parserModel); case SyntaxKind.CloseAngleBracketToken: if (typeClauseNode.GenericParametersListingNode is not null) { @@ -1770,4 +1813,111 @@ public IExpressionNode ParseNamedParameterSyntaxAndReturnEmptyExpressionNode(CSh return EmptyExpressionNode.Empty; } + + public IExpressionNode ParseAmbiguousIdentifierGenericParametersListingNode( + AmbiguousIdentifierExpressionNode ambiguousIdentifierExpressionNode, OpenAngleBracketToken openAngleBracketToken, CSharpCompilationUnit compilationUnit, ref CSharpParserModel parserModel) + { + if (ambiguousIdentifierExpressionNode.GenericParametersListingNode is null) + { + ambiguousIdentifierExpressionNode.SetGenericParametersListingNode( + new GenericParametersListingNode( + openAngleBracketToken, + new List(), + closeAngleBracketToken: default)); + } + + parserModel.ExpressionList.Add((SyntaxKind.CloseAngleBracketToken, ambiguousIdentifierExpressionNode)); + parserModel.ExpressionList.Add((SyntaxKind.CommaToken, ambiguousIdentifierExpressionNode.GenericParametersListingNode)); + return EmptyExpressionNode.Empty; + } + + public IExpressionNode ParseTypeClauseNodeGenericParametersListingNode( + TypeClauseNode typeClauseNode, OpenAngleBracketToken openAngleBracketToken, CSharpCompilationUnit compilationUnit, ref CSharpParserModel parserModel) + { + if (typeClauseNode.GenericParametersListingNode is null) + { + typeClauseNode.SetGenericParametersListingNode( + new GenericParametersListingNode( + openAngleBracketToken, + new List(), + closeAngleBracketToken: default)); + } + + parserModel.ExpressionList.Add((SyntaxKind.CloseAngleBracketToken, typeClauseNode)); + parserModel.ExpressionList.Add((SyntaxKind.CommaToken, typeClauseNode.GenericParametersListingNode)); + return EmptyExpressionNode.Empty; + } + + public void OpenLambdaExpressionScope(LambdaExpressionNode lambdaExpressionNode, OpenBraceToken openBraceToken, CSharpCompilationUnit compilationUnit, ref CSharpParserModel parserModel) + { + parserModel.CurrentCodeBlockBuilder.InnerPendingCodeBlockOwner = lambdaExpressionNode; + parserModel.CurrentCodeBlockBuilder.InnerPendingCodeBlockOwner.SetOpenBraceToken(openBraceToken, parserModel.DiagnosticBag, parserModel.TokenWalker); + + var nextCodeBlockOwner = parserModel.CurrentCodeBlockBuilder.InnerPendingCodeBlockOwner; + var nextReturnTypeClauseNode = nextCodeBlockOwner.GetReturnTypeClauseNode(); + + compilationUnit.Binder.OpenScope(nextCodeBlockOwner, nextReturnTypeClauseNode, openBraceToken.TextSpan, compilationUnit); + parserModel.CurrentCodeBlockBuilder = new(parent: parserModel.CurrentCodeBlockBuilder, codeBlockOwner: nextCodeBlockOwner); + compilationUnit.Binder.OnBoundScopeCreatedAndSetAsCurrent(nextCodeBlockOwner, compilationUnit); + } + + public void CloseLambdaExpressionScope(LambdaExpressionNode lambdaExpressionNode, CSharpCompilationUnit compilationUnit, ref CSharpParserModel parserModel) + { + var closeBraceToken = new CloseBraceToken(parserModel.TokenWalker.Current.TextSpan); + + if (parserModel.CurrentCodeBlockBuilder.CodeBlockOwner is not null) + parserModel.CurrentCodeBlockBuilder.CodeBlockOwner.SetCloseBraceToken(closeBraceToken, parserModel.DiagnosticBag, parserModel.TokenWalker); + + compilationUnit.Binder.CloseScope(closeBraceToken.TextSpan, compilationUnit, ref parserModel); + } + + /// + /// TODO: Parse the lambda expression's statements... + /// ...This sounds quite complicated because we went + /// from the statement-loop to the expression-loop + /// and now have to run the statement-loop again + /// but not lose the state of any active loops. + /// For now skip tokens until the close brace token is matched in order to + /// preserve the other features of the text editor. + /// (rather than lambda expression statements clobbering the entire syntax highlighting of the file). + /// + public void SkipLambdaExpressionStatements(LambdaExpressionNode lambdaExpressionNode, CSharpCompilationUnit compilationUnit, ref CSharpParserModel parserModel) + { + #if DEBUG + parserModel.TokenWalker.SuppressProtectedSyntaxKindConsumption = true; + #endif + + parserModel.TokenWalker.Consume(); // Skip the EqualsCloseAngleBracketToken + + var openBraceToken = (OpenBraceToken)parserModel.TokenWalker.Consume(); + + var openBraceCounter = 1; + + while (true) + { + if (parserModel.TokenWalker.IsEof) + break; + + if (parserModel.TokenWalker.Current.SyntaxKind == SyntaxKind.OpenBraceToken) + { + ++openBraceCounter; + } + else if (parserModel.TokenWalker.Current.SyntaxKind == SyntaxKind.CloseBraceToken) + { + if (--openBraceCounter <= 0) + break; + } + + _ = parserModel.TokenWalker.Consume(); + } + + CloseLambdaExpressionScope(lambdaExpressionNode, compilationUnit, ref parserModel); + + var closeTokenIndex = parserModel.TokenWalker.Index; + var closeBraceToken = (CloseBraceToken)parserModel.TokenWalker.Match(SyntaxKind.CloseBraceToken); + + #if DEBUG + parserModel.TokenWalker.SuppressProtectedSyntaxKindConsumption = false; + #endif + } } diff --git a/Source/Lib/CompilerServices/CSharp/BinderCase/CSharpBinder.Main.cs b/Source/Lib/CompilerServices/CSharp/BinderCase/CSharpBinder.Main.cs index b6f156035..6e46e9998 100644 --- a/Source/Lib/CompilerServices/CSharp/BinderCase/CSharpBinder.Main.cs +++ b/Source/Lib/CompilerServices/CSharp/BinderCase/CSharpBinder.Main.cs @@ -245,6 +245,16 @@ public void BindStringVerbatimExpression( DecorationByte = (byte)GenericDecorationKind.StringLiteral, }), compilationUnit); } + + public void BindDiscard( + IdentifierToken identifierToken, + CSharpCompilationUnit compilationUnit) + { + AddSymbolReference(new DiscardSymbol(identifierToken.TextSpan with + { + DecorationByte = (byte)GenericDecorationKind.None, + }), compilationUnit); + } public void BindFunctionDefinitionNode( FunctionDefinitionNode functionDefinitionNode, @@ -1777,5 +1787,13 @@ public void OnBoundScopeCreatedAndSetAsCurrent(ICodeBlockOwner codeBlockOwner, C compilationUnit.Binder.BindVariableDeclarationNode(argument.VariableDeclarationNode, compilationUnit); } } + else if (codeBlockOwner.SyntaxKind == SyntaxKind.LambdaExpressionNode) + { + var lambdaExpressionNode = (LambdaExpressionNode)codeBlockOwner; + foreach (var variableDeclarationNode in lambdaExpressionNode.VariableDeclarationNodeList) + { + compilationUnit.Binder.BindVariableDeclarationNode(variableDeclarationNode, compilationUnit); + } + } } } diff --git a/Source/Lib/CompilerServices/CSharp/LexerCase/CSharpLexer.cs b/Source/Lib/CompilerServices/CSharp/LexerCase/CSharpLexer.cs index c383b3ae0..b16d952b8 100644 --- a/Source/Lib/CompilerServices/CSharp/LexerCase/CSharpLexer.cs +++ b/Source/Lib/CompilerServices/CSharp/LexerCase/CSharpLexer.cs @@ -118,7 +118,11 @@ public static CSharpLexerOutput Lex(ResourceUri resourceUri, string sourceText) case '+': if (stringWalker.PeekCharacter(1) == '+') { - LexPlusPlusToken(ref lexerOutput, ref stringWalker); + var entryPositionIndex = stringWalker.PositionIndex; + stringWalker.ReadCharacter(); + stringWalker.ReadCharacter(); + var textSpan = new TextEditorTextSpan(entryPositionIndex, stringWalker.PositionIndex, (byte)GenericDecorationKind.None, stringWalker.ResourceUri, stringWalker.SourceText); + lexerOutput.SyntaxTokenList.Add(new PlusPlusToken(textSpan)); } else { @@ -131,7 +135,11 @@ public static CSharpLexerOutput Lex(ResourceUri resourceUri, string sourceText) case '-': if (stringWalker.PeekCharacter(1) == '-') { - LexMinusMinusToken(ref lexerOutput, ref stringWalker); + var entryPositionIndex = stringWalker.PositionIndex; + stringWalker.ReadCharacter(); + stringWalker.ReadCharacter(); + var textSpan = new TextEditorTextSpan(entryPositionIndex, stringWalker.PositionIndex, (byte)GenericDecorationKind.None, stringWalker.ResourceUri, stringWalker.SourceText); + lexerOutput.SyntaxTokenList.Add(new MinusMinusToken(textSpan)); } else { @@ -144,8 +152,20 @@ public static CSharpLexerOutput Lex(ResourceUri resourceUri, string sourceText) case '=': if (stringWalker.PeekCharacter(1) == '=') { - LexEqualsEqualsToken(ref lexerOutput, ref stringWalker); + var entryPositionIndex = stringWalker.PositionIndex; + stringWalker.ReadCharacter(); + stringWalker.ReadCharacter(); + var textSpan = new TextEditorTextSpan(entryPositionIndex, stringWalker.PositionIndex, (byte)GenericDecorationKind.None, stringWalker.ResourceUri, stringWalker.SourceText); + lexerOutput.SyntaxTokenList.Add(new EqualsEqualsToken(textSpan)); } + else if (stringWalker.PeekCharacter(1) == '>') + { + var entryPositionIndex = stringWalker.PositionIndex; + stringWalker.ReadCharacter(); + stringWalker.ReadCharacter(); + var textSpan = new TextEditorTextSpan(entryPositionIndex, stringWalker.PositionIndex, (byte)GenericDecorationKind.None, stringWalker.ResourceUri, stringWalker.SourceText); + lexerOutput.SyntaxTokenList.Add(new EqualsCloseAngleBracketToken(textSpan)); + } else { var entryPositionIndex = stringWalker.PositionIndex; @@ -157,7 +177,11 @@ public static CSharpLexerOutput Lex(ResourceUri resourceUri, string sourceText) case '?': if (stringWalker.PeekCharacter(1) == '?') { - LexQuestionMarkQuestionMarkToken(ref lexerOutput, ref stringWalker); + var entryPositionIndex = stringWalker.PositionIndex; + stringWalker.ReadCharacter(); + stringWalker.ReadCharacter(); + var textSpan = new TextEditorTextSpan(entryPositionIndex, stringWalker.PositionIndex, (byte)GenericDecorationKind.None, stringWalker.ResourceUri, stringWalker.SourceText); + lexerOutput.SyntaxTokenList.Add(new QuestionMarkQuestionMarkToken(textSpan)); } else { @@ -211,10 +235,21 @@ public static CSharpLexerOutput Lex(ResourceUri resourceUri, string sourceText) } case '!': { - var entryPositionIndex = stringWalker.PositionIndex; - stringWalker.ReadCharacter(); - var textSpan = new TextEditorTextSpan(entryPositionIndex, stringWalker.PositionIndex, (byte)GenericDecorationKind.None, stringWalker.ResourceUri, stringWalker.SourceText); - lexerOutput.SyntaxTokenList.Add(new BangToken(textSpan)); + if (stringWalker.PeekCharacter(1) == '=') + { + var entryPositionIndex = stringWalker.PositionIndex; + stringWalker.ReadCharacter(); + stringWalker.ReadCharacter(); + var textSpan = new TextEditorTextSpan(entryPositionIndex, stringWalker.PositionIndex, (byte)GenericDecorationKind.None, stringWalker.ResourceUri, stringWalker.SourceText); + lexerOutput.SyntaxTokenList.Add(new BangEqualsToken(textSpan)); + } + else + { + var entryPositionIndex = stringWalker.PositionIndex; + stringWalker.ReadCharacter(); + var textSpan = new TextEditorTextSpan(entryPositionIndex, stringWalker.PositionIndex, (byte)GenericDecorationKind.None, stringWalker.ResourceUri, stringWalker.SourceText); + lexerOutput.SyntaxTokenList.Add(new BangToken(textSpan)); + } break; } case ';': @@ -259,18 +294,40 @@ public static CSharpLexerOutput Lex(ResourceUri resourceUri, string sourceText) } case '<': { - var entryPositionIndex = stringWalker.PositionIndex; - stringWalker.ReadCharacter(); - var textSpan = new TextEditorTextSpan(entryPositionIndex, stringWalker.PositionIndex, (byte)GenericDecorationKind.None, stringWalker.ResourceUri, stringWalker.SourceText); - lexerOutput.SyntaxTokenList.Add(new OpenAngleBracketToken(textSpan)); + if (stringWalker.PeekCharacter(1) == '=') + { + var entryPositionIndex = stringWalker.PositionIndex; + stringWalker.ReadCharacter(); + stringWalker.ReadCharacter(); + var textSpan = new TextEditorTextSpan(entryPositionIndex, stringWalker.PositionIndex, (byte)GenericDecorationKind.None, stringWalker.ResourceUri, stringWalker.SourceText); + lexerOutput.SyntaxTokenList.Add(new OpenAngleBracketEqualsToken(textSpan)); + } + else + { + var entryPositionIndex = stringWalker.PositionIndex; + stringWalker.ReadCharacter(); + var textSpan = new TextEditorTextSpan(entryPositionIndex, stringWalker.PositionIndex, (byte)GenericDecorationKind.None, stringWalker.ResourceUri, stringWalker.SourceText); + lexerOutput.SyntaxTokenList.Add(new OpenAngleBracketToken(textSpan)); + } break; } case '>': { - var entryPositionIndex = stringWalker.PositionIndex; - stringWalker.ReadCharacter(); - var textSpan = new TextEditorTextSpan(entryPositionIndex, stringWalker.PositionIndex, (byte)GenericDecorationKind.None, stringWalker.ResourceUri, stringWalker.SourceText); - lexerOutput.SyntaxTokenList.Add(new CloseAngleBracketToken(textSpan)); + if (stringWalker.PeekCharacter(1) == '=') + { + var entryPositionIndex = stringWalker.PositionIndex; + stringWalker.ReadCharacter(); + stringWalker.ReadCharacter(); + var textSpan = new TextEditorTextSpan(entryPositionIndex, stringWalker.PositionIndex, (byte)GenericDecorationKind.None, stringWalker.ResourceUri, stringWalker.SourceText); + lexerOutput.SyntaxTokenList.Add(new CloseAngleBracketEqualsToken(textSpan)); + } + else + { + var entryPositionIndex = stringWalker.PositionIndex; + stringWalker.ReadCharacter(); + var textSpan = new TextEditorTextSpan(entryPositionIndex, stringWalker.PositionIndex, (byte)GenericDecorationKind.None, stringWalker.ResourceUri, stringWalker.SourceText); + lexerOutput.SyntaxTokenList.Add(new CloseAngleBracketToken(textSpan)); + } break; } case '[': @@ -329,16 +386,30 @@ public static CSharpLexerOutput Lex(ResourceUri resourceUri, string sourceText) } else { - LexDollarSignToken(ref lexerOutput, ref stringWalker); + var entryPositionIndex = stringWalker.PositionIndex; + stringWalker.ReadCharacter(); + var textSpan = new TextEditorTextSpan(entryPositionIndex, stringWalker.PositionIndex, (byte)GenericDecorationKind.None, stringWalker.ResourceUri, stringWalker.SourceText); + lexerOutput.SyntaxTokenList.Add(new DollarSignToken(textSpan)); + break; } break; case '@': if (stringWalker.NextCharacter == '"') + { LexString(ref lexerOutput, ref stringWalker, ref decorationByteLastEscapeCharacter, countDollarSign: 0, useVerbatim: true); + } else if (stringWalker.PeekCharacter(1) == '$' && stringWalker.PeekCharacter(2) == '"') + { LexString(ref lexerOutput, ref stringWalker, ref decorationByteLastEscapeCharacter, countDollarSign: 1, useVerbatim: true); + } else - LexAtToken(ref lexerOutput, ref stringWalker); + { + var entryPositionIndex = stringWalker.PositionIndex; + stringWalker.ReadCharacter(); + var textSpan = new TextEditorTextSpan(entryPositionIndex, stringWalker.PositionIndex, (byte)GenericDecorationKind.None, stringWalker.ResourceUri, stringWalker.SourceText); + lexerOutput.SyntaxTokenList.Add(new AtToken(textSpan)); + break; + } break; case ':': { @@ -827,114 +898,6 @@ public static void LexCommentMultiLineToken(ref CSharpLexerOutput lexerOutput, r lexerOutput.SyntaxTokenList.Add(new CommentMultiLineToken(textSpan)); } - public static void LexPlusPlusToken(ref CSharpLexerOutput lexerOutput, ref StringWalkerStruct stringWalker) - { - var entryPositionIndex = stringWalker.PositionIndex; - - // First '+' - stringWalker.ReadCharacter(); - // Second '+' - stringWalker.ReadCharacter(); - - var textSpan = new TextEditorTextSpan( - entryPositionIndex, - stringWalker.PositionIndex, - (byte)GenericDecorationKind.None, - stringWalker.ResourceUri, - stringWalker.SourceText); - - lexerOutput.SyntaxTokenList.Add(new PlusPlusToken(textSpan)); - } - - public static void LexMinusMinusToken(ref CSharpLexerOutput lexerOutput, ref StringWalkerStruct stringWalker) - { - var entryPositionIndex = stringWalker.PositionIndex; - - // First '-' - stringWalker.ReadCharacter(); - // Second '-' - stringWalker.ReadCharacter(); - - var textSpan = new TextEditorTextSpan( - entryPositionIndex, - stringWalker.PositionIndex, - (byte)GenericDecorationKind.None, - stringWalker.ResourceUri, - stringWalker.SourceText); - - lexerOutput.SyntaxTokenList.Add(new MinusMinusToken(textSpan)); - } - - public static void LexEqualsEqualsToken(ref CSharpLexerOutput lexerOutput, ref StringWalkerStruct stringWalker) - { - var entryPositionIndex = stringWalker.PositionIndex; - - // First '=' - stringWalker.ReadCharacter(); - // Second '=' - stringWalker.ReadCharacter(); - - var textSpan = new TextEditorTextSpan( - entryPositionIndex, - stringWalker.PositionIndex, - (byte)GenericDecorationKind.None, - stringWalker.ResourceUri, - stringWalker.SourceText); - - lexerOutput.SyntaxTokenList.Add(new EqualsEqualsToken(textSpan)); - } - - public static void LexQuestionMarkQuestionMarkToken(ref CSharpLexerOutput lexerOutput, ref StringWalkerStruct stringWalker) - { - var entryPositionIndex = stringWalker.PositionIndex; - - // First '?' - stringWalker.ReadCharacter(); - // Second '?' - stringWalker.ReadCharacter(); - - var textSpan = new TextEditorTextSpan( - entryPositionIndex, - stringWalker.PositionIndex, - (byte)GenericDecorationKind.None, - stringWalker.ResourceUri, - stringWalker.SourceText); - - lexerOutput.SyntaxTokenList.Add(new QuestionMarkQuestionMarkToken(textSpan)); - } - - public static void LexDollarSignToken(ref CSharpLexerOutput lexerOutput, ref StringWalkerStruct stringWalker) - { - var entryPositionIndex = stringWalker.PositionIndex; - - stringWalker.ReadCharacter(); - - var textSpan = new TextEditorTextSpan( - entryPositionIndex, - stringWalker.PositionIndex, - (byte)GenericDecorationKind.None, - stringWalker.ResourceUri, - stringWalker.SourceText); - - lexerOutput.SyntaxTokenList.Add(new DollarSignToken(textSpan)); - } - - public static void LexAtToken(ref CSharpLexerOutput lexerOutput, ref StringWalkerStruct stringWalker) - { - var entryPositionIndex = stringWalker.PositionIndex; - - stringWalker.ReadCharacter(); - - var textSpan = new TextEditorTextSpan( - entryPositionIndex, - stringWalker.PositionIndex, - (byte)GenericDecorationKind.None, - stringWalker.ResourceUri, - stringWalker.SourceText); - - lexerOutput.SyntaxTokenList.Add(new AtToken(textSpan)); - } - public static void LexPreprocessorDirectiveToken(ref CSharpLexerOutput lexerOutput, ref StringWalkerStruct stringWalker) { var entryPositionIndex = stringWalker.PositionIndex; diff --git a/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/ParseFunctions.cs b/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/ParseFunctions.cs index f1e520ff4..c2d6cd169 100644 --- a/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/ParseFunctions.cs +++ b/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/ParseFunctions.cs @@ -146,8 +146,7 @@ public static FunctionArgumentsListingNode HandleFunctionArguments(CSharpCompila { break; } - else if (parserModel.TokenWalker.Current.SyntaxKind == SyntaxKind.EqualsToken && - parserModel.TokenWalker.Next.SyntaxKind == SyntaxKind.CloseAngleBracketToken) + else if (parserModel.TokenWalker.Current.SyntaxKind == SyntaxKind.EqualsCloseAngleBracketToken) { break; } diff --git a/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/ParseTokens.cs b/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/ParseTokens.cs index ca3c4a0c4..c530d5d66 100644 --- a/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/ParseTokens.cs +++ b/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/ParseTokens.cs @@ -23,6 +23,22 @@ public static void ParsePreprocessorDirectiveToken( public static void ParseIdentifierToken(CSharpCompilationUnit compilationUnit, ref CSharpParserModel parserModel) { + if (parserModel.TokenWalker.Current.TextSpan.Length == 1 && + parserModel.TokenWalker.Current.TextSpan.GetText() == "_") + { + if (!compilationUnit.Binder.TryGetVariableDeclarationHierarchically( + compilationUnit, + compilationUnit.BinderSession.ResourceUri, + compilationUnit.BinderSession.CurrentScopeIndexKey, + parserModel.TokenWalker.Current.TextSpan.GetText(), + out _)) + { + compilationUnit.Binder.BindDiscard((IdentifierToken)parserModel.TokenWalker.Current, compilationUnit); + _ = parserModel.TokenWalker.Consume(); + return; + } + } + var originalTokenIndex = parserModel.TokenWalker.Index; parserModel.TryParseExpressionSyntaxKindList.Add(SyntaxKind.TypeClauseNode); @@ -71,6 +87,21 @@ public static void ParseIdentifierToken(CSharpCompilationUnit compilationUnit, r MoveToHandleVariableDeclarationNode((VariableDeclarationNode)expressionNode, compilationUnit, ref parserModel); return; case SyntaxKind.VariableReferenceNode: + + var isQuestionMarkMemberAccessToken = parserModel.TokenWalker.Current.SyntaxKind == SyntaxKind.QuestionMarkToken && + parserModel.TokenWalker.Next.SyntaxKind == SyntaxKind.MemberAccessToken; + + if ((parserModel.TokenWalker.Current.SyntaxKind == SyntaxKind.MemberAccessToken || isQuestionMarkMemberAccessToken) && + originalTokenIndex == parserModel.TokenWalker.Index - 1) + { + _ = parserModel.TokenWalker.Backtrack(); + expressionNode = ParseOthers.ParseExpression(compilationUnit, ref parserModel); + parserModel.StatementBuilder.ChildList.Add(expressionNode); + return; + } + + parserModel.StatementBuilder.ChildList.Add(expressionNode); + return; case SyntaxKind.FunctionInvocationNode: case SyntaxKind.ConstructorInvocationExpressionNode: parserModel.StatementBuilder.ChildList.Add(expressionNode); @@ -96,8 +127,7 @@ public static void MoveToHandleVariableDeclarationNode(IVariableDeclarationNode var variableKind = VariableKind.Local; if (parserModel.TokenWalker.Current.SyntaxKind == SyntaxKind.OpenBraceToken || - (parserModel.TokenWalker.Current.SyntaxKind == SyntaxKind.EqualsToken && - parserModel.TokenWalker.Next.SyntaxKind == SyntaxKind.CloseAngleBracketToken)) + parserModel.TokenWalker.Current.SyntaxKind == SyntaxKind.EqualsCloseAngleBracketToken) { variableKind = VariableKind.Property; } @@ -117,8 +147,7 @@ public static void MoveToHandleVariableDeclarationNode(IVariableDeclarationNode { ParsePropertyDefinition(compilationUnit, variableDeclarationNode, ref parserModel); } - else if (parserModel.TokenWalker.Current.SyntaxKind == SyntaxKind.EqualsToken && - parserModel.TokenWalker.Next.SyntaxKind == SyntaxKind.CloseAngleBracketToken) + else if (parserModel.TokenWalker.Current.SyntaxKind == SyntaxKind.EqualsCloseAngleBracketToken) { ParsePropertyDefinition_ExpressionBound(compilationUnit, ref parserModel); } @@ -202,8 +231,7 @@ public static void ParsePropertyDefinition(CSharpCompilationUnit compilationUnit public static void ParsePropertyDefinition_ExpressionBound(CSharpCompilationUnit compilationUnit, ref CSharpParserModel parserModel) { - var equalsToken = (EqualsToken)parserModel.TokenWalker.Consume(); - var closeAngleBracketToken = (CloseAngleBracketToken)parserModel.TokenWalker.Consume(); + var equalsCloseAngleBracketToken = (EqualsCloseAngleBracketToken)parserModel.TokenWalker.Consume(); var expressionNode = ParseOthers.ParseExpression(compilationUnit, ref parserModel); var statementDelimiterToken = (StatementDelimiterToken)parserModel.TokenWalker.Match(SyntaxKind.StatementDelimiterToken); diff --git a/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/ParseVariables.cs b/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/ParseVariables.cs index ddea2d038..888ba144d 100644 --- a/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/ParseVariables.cs +++ b/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/ParseVariables.cs @@ -63,24 +63,21 @@ public static void HandleVariableDeclarationStatement( if (parserModel.TokenWalker.Current.SyntaxKind == SyntaxKind.EqualsToken) { - if (parserModel.TokenWalker.Peek(1).SyntaxKind == SyntaxKind.CloseAngleBracketToken) - { - HandlePropertyExpression( - variableDeclarationNode, - (EqualsToken)parserModel.TokenWalker.Consume(), - (CloseAngleBracketToken)parserModel.TokenWalker.Consume(), - compilationUnit, - ref parserModel); - } - else - { - // Variable initialization occurs here. - HandleVariableAssignment( - consumedIdentifierToken, - (EqualsToken)parserModel.TokenWalker.Consume(), - compilationUnit, - ref parserModel); - } + // Variable initialization occurs here. + HandleVariableAssignment( + consumedIdentifierToken, + (EqualsToken)parserModel.TokenWalker.Consume(), + compilationUnit, + ref parserModel); + } + else if (parserModel.TokenWalker.Current.SyntaxKind == SyntaxKind.EqualsCloseAngleBracketToken) + { + HandlePropertyExpression( + variableDeclarationNode, + (EqualsToken)parserModel.TokenWalker.Consume(), + (CloseAngleBracketToken)parserModel.TokenWalker.Consume(), + compilationUnit, + ref parserModel); } else { diff --git a/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/UtilityApi.cs b/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/UtilityApi.cs index 53ea93f30..9bbd5c642 100644 --- a/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/UtilityApi.cs +++ b/Source/Lib/CompilerServices/CSharp/ParserCase/Internals/UtilityApi.cs @@ -81,9 +81,16 @@ public static bool IsBinaryOperatorSyntaxKind(SyntaxKind syntaxKind) case SyntaxKind.MinusToken: case SyntaxKind.StarToken: case SyntaxKind.DivisionToken: + case SyntaxKind.CloseAngleBracketToken: + case SyntaxKind.CloseAngleBracketEqualsToken: + case SyntaxKind.OpenAngleBracketToken: + case SyntaxKind.OpenAngleBracketEqualsToken: + case SyntaxKind.BangEqualsToken: case SyntaxKind.EqualsToken: case SyntaxKind.EqualsEqualsToken: + case SyntaxKind.AmpersandToken: case SyntaxKind.AmpersandAmpersandToken: + case SyntaxKind.PipeToken: case SyntaxKind.PipePipeToken: case SyntaxKind.QuestionMarkQuestionMarkToken: return true; diff --git a/Source/Lib/Ide/Ide.RazorLib/Commands/CommandFactory.cs b/Source/Lib/Ide/Ide.RazorLib/Commands/CommandFactory.cs index 0b579b20e..2b2699efc 100644 --- a/Source/Lib/Ide/Ide.RazorLib/Commands/CommandFactory.cs +++ b/Source/Lib/Ide/Ide.RazorLib/Commands/CommandFactory.cs @@ -22,7 +22,9 @@ using Luthetus.Common.RazorLib.Installations.Displays; using Luthetus.TextEditor.RazorLib; using Luthetus.TextEditor.RazorLib.Installations.Displays; +using Luthetus.TextEditor.RazorLib.Cursors.Models; using Luthetus.Ide.RazorLib.CodeSearches.Displays; +using Luthetus.Ide.RazorLib.CodeSearches.States; using Luthetus.Ide.RazorLib.Editors.Models; namespace Luthetus.Ide.RazorLib.Commands; @@ -371,6 +373,45 @@ await _jsRuntime.GetLuthetusCommonApi() null); _dispatcher.Dispatch(new DialogState.RegisterAction(CodeSearchDialog)); + + _textEditorService.PostUnique(nameof(CodeSearchDisplay), editContext => + { + var group = _textEditorService.GroupApi.GetOrDefault(EditorIdeApi.EditorTextEditorGroupKey); + if (group is null) + return Task.CompletedTask; + + var activeViewModel = _textEditorService.ViewModelApi.GetOrDefault(group.ActiveViewModelKey); + if (activeViewModel is null) + return Task.CompletedTask; + + var viewModelModifier = editContext.GetViewModelModifier(activeViewModel.ViewModelKey); + if (viewModelModifier is null) + return Task.CompletedTask; + + // If the user has an active text selection, + // then populate the code search with their selection. + + var modelModifier = editContext.GetModelModifier(viewModelModifier.ViewModel.ResourceUri); + var cursorModifierBag = editContext.GetCursorModifierBag(viewModelModifier?.ViewModel); + var primaryCursorModifier = editContext.GetPrimaryCursorModifier(cursorModifierBag); + + if (modelModifier is null || cursorModifierBag is null || primaryCursorModifier is null) + return Task.CompletedTask; + + var selectedText = TextEditorSelectionHelper.GetSelectedText(primaryCursorModifier, modelModifier); + if (selectedText is null) + return Task.CompletedTask; + + _dispatcher.Dispatch(new CodeSearchState.WithAction(inState => inState with + { + Query = selectedText, + })); + + _dispatcher.Dispatch(new CodeSearchState.SearchEffect()); + + return Task.CompletedTask; + }); + return Task.CompletedTask; }); diff --git a/Source/Lib/Ide/Ide.RazorLib/Luthetus.Ide.RazorLib.csproj b/Source/Lib/Ide/Ide.RazorLib/Luthetus.Ide.RazorLib.csproj index fe20ed7fa..fd0cd2ea7 100644 --- a/Source/Lib/Ide/Ide.RazorLib/Luthetus.Ide.RazorLib.csproj +++ b/Source/Lib/Ide/Ide.RazorLib/Luthetus.Ide.RazorLib.csproj @@ -4,7 +4,7 @@ net8.0 enable enable - 0.9.7.11 + 0.9.7.12 diff --git a/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/Internals/IdeInfoDisplay.razor b/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/Internals/IdeInfoDisplay.razor index 1c0a0718b..6723c3abe 100644 --- a/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/Internals/IdeInfoDisplay.razor +++ b/Source/Lib/Ide/Ide.RazorLib/Shareds/Displays/Internals/IdeInfoDisplay.razor @@ -172,6 +172,63 @@ Recent Changes:
+
v 0.9.7.12 (2024-12-22)
+
    +
  • + Show scope of lambda expression +
  • +
  • + Stop syntax highlighting member accesses as types erroneously. + (this turns off any syntax highlighting other than function invocation + but this turns out to be a massive quality of life improvement + while the types are being fully parsed). +
  • +
  • + DiscardSymbol (if '_' is a variable within scope it will be treated as + a variable otherwise it will be treated as 'discard'). +
  • +
  • + Fix: is / is not pattern matching 'oldEvent is not RedundantTextEditorWork oldRedundantTextEditorWork' syntax. +
  • +
  • + Fix: 'out' syntax +
  • +
  • + FindOverlayDisplay fix bug if already open select then keybind wasn't populating +
  • +
  • + Populate code search with text editor selection if there is one and keybind done from text editor focus +
  • +
  • + Populate FindAll search if opened while focused on the text editor and have a text selection +
  • +
  • + Identifiers as binary expression operands +
  • +
  • + Bind lambda expression variables within its scope (expression body, statement does not work). +
  • +
  • + SkipLambdaExpressionStatements(...) progress. + In short, I have a statement-loop, and inside of that is the expression-loop. + + So, for Lambdas with statement-bodies, I need to go from the + statement-loop -> expression-loop -> statement-loop + + All while not losing the state of any already existing loops. So it + seems a bit complicated. (the parser is written via primitive recursion with + a while loop, and I'd prefer to keep it that way. + This might have to be an exception though I'm not sure). + + I want to focus on having the features that work... work + rather than having the lambda statements clobber features + that come after it (in a text file). + + Lambda statements will be supported in the future though. +
  • +
+
+
v 0.9.7.11 (2024-12-20)
  • diff --git a/Source/Lib/TextEditor/Commands/Models/Defaults/TextEditorCommandDefaultFacts.cs b/Source/Lib/TextEditor/Commands/Models/Defaults/TextEditorCommandDefaultFacts.cs index dd4fe5649..f976d44a0 100644 --- a/Source/Lib/TextEditor/Commands/Models/Defaults/TextEditorCommandDefaultFacts.cs +++ b/Source/Lib/TextEditor/Commands/Models/Defaults/TextEditorCommandDefaultFacts.cs @@ -7,6 +7,7 @@ using Luthetus.TextEditor.RazorLib.Edits.Models; using Luthetus.TextEditor.RazorLib.Lexers.Models; using Luthetus.TextEditor.RazorLib.Exceptions; +using Luthetus.TextEditor.RazorLib.FindAlls.States; namespace Luthetus.TextEditor.RazorLib.Commands.Models.Defaults; @@ -710,29 +711,27 @@ public static class TextEditorCommandDefaultFacts var commandArgs = (TextEditorCommandArgs)interfaceCommandArgs; var viewModelModifier = commandArgs.EditContext.GetViewModelModifier(commandArgs.ViewModelKey); - if (viewModelModifier is null) return; // If the user has an active text selection, // then populate the find overlay with their selection. + + var modelModifier = commandArgs.EditContext.GetModelModifier(commandArgs.ModelResourceUri); + var cursorModifierBag = commandArgs.EditContext.GetCursorModifierBag(viewModelModifier?.ViewModel); + var primaryCursorModifier = commandArgs.EditContext.GetPrimaryCursorModifier(cursorModifierBag); + + if (modelModifier is null || cursorModifierBag is null || primaryCursorModifier is null) + return; + + var selectedText = TextEditorSelectionHelper.GetSelectedText(primaryCursorModifier, modelModifier); + if (selectedText is not null) { - var modelModifier = commandArgs.EditContext.GetModelModifier(commandArgs.ModelResourceUri); - var cursorModifierBag = commandArgs.EditContext.GetCursorModifierBag(viewModelModifier?.ViewModel); - var primaryCursorModifier = commandArgs.EditContext.GetPrimaryCursorModifier(cursorModifierBag); - - if (modelModifier is null || cursorModifierBag is null || primaryCursorModifier is null) - return; - - var selectedText = TextEditorSelectionHelper.GetSelectedText(primaryCursorModifier, modelModifier); - - if (selectedText is not null) - { - viewModelModifier.ViewModel = viewModelModifier.ViewModel with - { - FindOverlayValue = selectedText, - }; - } + viewModelModifier.ViewModel = viewModelModifier.ViewModel with + { + FindOverlayValue = selectedText, + FindOverlayValueExternallyChangedMarker = !viewModelModifier.ViewModel.FindOverlayValueExternallyChangedMarker, + }; } if (viewModelModifier.ViewModel.ShowFindOverlay) @@ -748,8 +747,34 @@ await commandArgs.ServiceProvider.GetRequiredService().GetLuthetusCo ShowFindOverlay = true, }; } - - return; + }); + + public static readonly TextEditorCommand PopulateSearchFindAll = new( + "PopulateSearchFindAll", "defaults_populate-search-find-all", false, true, TextEditKind.None, null, + async interfaceCommandArgs => + { + var commandArgs = (TextEditorCommandArgs)interfaceCommandArgs; + + var viewModelModifier = commandArgs.EditContext.GetViewModelModifier(commandArgs.ViewModelKey); + + if (viewModelModifier is null) + return; + + // If the user has an active text selection, + // then populate the find overlay with their selection. + + var modelModifier = commandArgs.EditContext.GetModelModifier(commandArgs.ModelResourceUri); + var cursorModifierBag = commandArgs.EditContext.GetCursorModifierBag(viewModelModifier?.ViewModel); + var primaryCursorModifier = commandArgs.EditContext.GetPrimaryCursorModifier(cursorModifierBag); + + if (modelModifier is null || cursorModifierBag is null || primaryCursorModifier is null) + return; + + var selectedText = TextEditorSelectionHelper.GetSelectedText(primaryCursorModifier, modelModifier); + if (selectedText is null) + return; + + commandArgs.ComponentData.Dispatcher.Dispatch(new TextEditorFindAllState.SetSearchQueryAction(selectedText)); }); /// diff --git a/Source/Lib/TextEditor/CompilerServices/Syntax/Nodes/LambdaExpressionNode.cs b/Source/Lib/TextEditor/CompilerServices/Syntax/Nodes/LambdaExpressionNode.cs index 71f17cdc4..55f1a8b53 100644 --- a/Source/Lib/TextEditor/CompilerServices/Syntax/Nodes/LambdaExpressionNode.cs +++ b/Source/Lib/TextEditor/CompilerServices/Syntax/Nodes/LambdaExpressionNode.cs @@ -1,5 +1,6 @@ using System.Collections.Immutable; using Luthetus.TextEditor.RazorLib.CompilerServices; +using Luthetus.TextEditor.RazorLib.CompilerServices.Utility; using Luthetus.TextEditor.RazorLib.CompilerServices.Interfaces; using Luthetus.TextEditor.RazorLib.CompilerServices.Syntax.Nodes.Interfaces; using Luthetus.TextEditor.RazorLib.CompilerServices.Syntax.Nodes.Enums; @@ -22,7 +23,7 @@ namespace Luthetus.TextEditor.RazorLib.CompilerServices.Syntax.Nodes; /// made, and a 'MethodGroupExpressionNode' (this type does not yet exist) will be returned as the /// primary expression. /// -public sealed class LambdaExpressionNode : IExpressionNode +public sealed class LambdaExpressionNode : IExpressionNode, ICodeBlockOwner { public LambdaExpressionNode(TypeClauseNode resultTypeClauseNode) { @@ -48,6 +49,61 @@ public LambdaExpressionNode(TypeClauseNode resultTypeClauseNode) public bool IsFabricated { get; init; } public SyntaxKind SyntaxKind => SyntaxKind.LambdaExpressionNode; + public ScopeDirectionKind ScopeDirectionKind => ScopeDirectionKind.Down; + public OpenBraceToken OpenBraceToken { get; private set; } + public CloseBraceToken CloseBraceToken { get; private set; } + public StatementDelimiterToken StatementDelimiterToken { get; private set; } + public CodeBlockNode? CodeBlockNode { get; private set; } + public bool IsSingleStatementBody => StatementDelimiterToken.ConstructorWasInvoked; + public TypeClauseNode ReturnTypeClauseNode { get; } + + public TypeClauseNode? GetReturnTypeClauseNode() + { + return ReturnTypeClauseNode; + } + + // (2024-11-08) + public ICodeBlockOwner SetOpenBraceToken(OpenBraceToken openBraceToken, DiagnosticBag diagnosticBag, TokenWalker tokenWalker) + { + if (StatementDelimiterToken.ConstructorWasInvoked) + ICodeBlockOwner.ThrowMultipleScopeDelimiterException(diagnosticBag, tokenWalker); + + OpenBraceToken = openBraceToken; + + _childListIsDirty = true; + return this; + } + public ICodeBlockOwner SetCloseBraceToken(CloseBraceToken closeBraceToken, DiagnosticBag diagnosticBag, TokenWalker tokenWalker) + { + if (StatementDelimiterToken.ConstructorWasInvoked) + ICodeBlockOwner.ThrowMultipleScopeDelimiterException(diagnosticBag, tokenWalker); + + CloseBraceToken = closeBraceToken; + + _childListIsDirty = true; + return this; + } + public ICodeBlockOwner SetStatementDelimiterToken(StatementDelimiterToken statementDelimiterToken, DiagnosticBag diagnosticBag, TokenWalker tokenWalker) + { + if (OpenBraceToken.ConstructorWasInvoked || CloseBraceToken.ConstructorWasInvoked) + ICodeBlockOwner.ThrowMultipleScopeDelimiterException(diagnosticBag, tokenWalker); + + StatementDelimiterToken = statementDelimiterToken; + + _childListIsDirty = true; + return this; + } + public ICodeBlockOwner SetCodeBlockNode(CodeBlockNode codeBlockNode, DiagnosticBag diagnosticBag, TokenWalker tokenWalker) + { + if (CodeBlockNode is not null) + ICodeBlockOwner.ThrowAlreadyAssignedCodeBlockNodeException(diagnosticBag, tokenWalker); + + CodeBlockNode = codeBlockNode; + + _childListIsDirty = true; + return this; + } + public void AddVariableDeclarationNode(IVariableDeclarationNode variableDeclarationNode) { VariableDeclarationNodeList.Add(variableDeclarationNode); diff --git a/Source/Lib/TextEditor/CompilerServices/Syntax/Symbols/DiscardSymbol.cs b/Source/Lib/TextEditor/CompilerServices/Syntax/Symbols/DiscardSymbol.cs new file mode 100644 index 000000000..0a85fe79e --- /dev/null +++ b/Source/Lib/TextEditor/CompilerServices/Syntax/Symbols/DiscardSymbol.cs @@ -0,0 +1,16 @@ +using Luthetus.TextEditor.RazorLib.Lexers.Models; + +namespace Luthetus.TextEditor.RazorLib.CompilerServices.Syntax.Symbols; + +public record struct DiscardSymbol : ISymbol +{ + public DiscardSymbol(TextEditorTextSpan textSpan) + { + TextSpan = textSpan; + } + + public TextEditorTextSpan TextSpan { get; } + public string SymbolKindString => SyntaxKind.ToString(); + + public SyntaxKind SyntaxKind => SyntaxKind.DiscardSymbol; +} diff --git a/Source/Lib/TextEditor/CompilerServices/Syntax/SyntaxKind.cs b/Source/Lib/TextEditor/CompilerServices/Syntax/SyntaxKind.cs index 13bca3be3..2188155e0 100644 --- a/Source/Lib/TextEditor/CompilerServices/Syntax/SyntaxKind.cs +++ b/Source/Lib/TextEditor/CompilerServices/Syntax/SyntaxKind.cs @@ -26,13 +26,17 @@ public enum SyntaxKind DivisionToken, EqualsToken, EqualsEqualsToken, + EqualsCloseAngleBracketToken, QuestionMarkToken, QuestionMarkQuestionMarkToken, PipeToken, PipePipeToken, AmpersandToken, AmpersandAmpersandToken, + CloseAngleBracketEqualsToken, + OpenAngleBracketEqualsToken, BangToken, + BangEqualsToken, StatementDelimiterToken, ArraySyntaxToken, AssociatedNameToken, @@ -250,6 +254,7 @@ public enum SyntaxKind // Symbols TypeSymbol, ConstructorSymbol, + DiscardSymbol, FunctionSymbol, LambdaSymbol, VariableSymbol, diff --git a/Source/Lib/TextEditor/CompilerServices/Syntax/Tokens/BangEqualsToken.cs b/Source/Lib/TextEditor/CompilerServices/Syntax/Tokens/BangEqualsToken.cs new file mode 100644 index 000000000..ab8010551 --- /dev/null +++ b/Source/Lib/TextEditor/CompilerServices/Syntax/Tokens/BangEqualsToken.cs @@ -0,0 +1,17 @@ +using Luthetus.TextEditor.RazorLib.Lexers.Models; + +namespace Luthetus.TextEditor.RazorLib.CompilerServices.Syntax.Tokens; + +public struct BangEqualsToken : ISyntaxToken +{ + public BangEqualsToken(TextEditorTextSpan textSpan) + { + ConstructorWasInvoked = true; + TextSpan = textSpan; + } + + public TextEditorTextSpan TextSpan { get; } + public SyntaxKind SyntaxKind => SyntaxKind.BangEqualsToken; + public bool IsFabricated { get; init; } + public bool ConstructorWasInvoked { get; } +} diff --git a/Source/Lib/TextEditor/CompilerServices/Syntax/Tokens/CloseAngleBracketEqualsToken.cs b/Source/Lib/TextEditor/CompilerServices/Syntax/Tokens/CloseAngleBracketEqualsToken.cs new file mode 100644 index 000000000..b6a7c0e75 --- /dev/null +++ b/Source/Lib/TextEditor/CompilerServices/Syntax/Tokens/CloseAngleBracketEqualsToken.cs @@ -0,0 +1,17 @@ +using Luthetus.TextEditor.RazorLib.Lexers.Models; + +namespace Luthetus.TextEditor.RazorLib.CompilerServices.Syntax.Tokens; + +public struct CloseAngleBracketEqualsToken : ISyntaxToken +{ + public CloseAngleBracketEqualsToken(TextEditorTextSpan textSpan) + { + ConstructorWasInvoked = true; + TextSpan = textSpan; + } + + public TextEditorTextSpan TextSpan { get; } + public SyntaxKind SyntaxKind => SyntaxKind.CloseAngleBracketEqualsToken; + public bool IsFabricated { get; init; } + public bool ConstructorWasInvoked { get; } +} diff --git a/Source/Lib/TextEditor/CompilerServices/Syntax/Tokens/EqualsCloseAngleBracketToken.cs b/Source/Lib/TextEditor/CompilerServices/Syntax/Tokens/EqualsCloseAngleBracketToken.cs new file mode 100644 index 000000000..9193e1391 --- /dev/null +++ b/Source/Lib/TextEditor/CompilerServices/Syntax/Tokens/EqualsCloseAngleBracketToken.cs @@ -0,0 +1,18 @@ +using Luthetus.TextEditor.RazorLib.Lexers.Models; + +namespace Luthetus.TextEditor.RazorLib.CompilerServices.Syntax.Tokens; + +public struct EqualsCloseAngleBracketToken : ISyntaxToken +{ + public EqualsCloseAngleBracketToken(TextEditorTextSpan textSpan) + { + ConstructorWasInvoked = true; + TextSpan = textSpan; + } + + public TextEditorTextSpan TextSpan { get; } + public SyntaxKind SyntaxKind => SyntaxKind.EqualsCloseAngleBracketToken; + public bool IsFabricated { get; init; } + public bool ConstructorWasInvoked { get; } +} + diff --git a/Source/Lib/TextEditor/CompilerServices/Syntax/Tokens/OpenAngleBracketEqualsToken.cs b/Source/Lib/TextEditor/CompilerServices/Syntax/Tokens/OpenAngleBracketEqualsToken.cs new file mode 100644 index 000000000..0069cc740 --- /dev/null +++ b/Source/Lib/TextEditor/CompilerServices/Syntax/Tokens/OpenAngleBracketEqualsToken.cs @@ -0,0 +1,17 @@ +using Luthetus.TextEditor.RazorLib.Lexers.Models; + +namespace Luthetus.TextEditor.RazorLib.CompilerServices.Syntax.Tokens; + +public struct OpenAngleBracketEqualsToken : ISyntaxToken +{ + public OpenAngleBracketEqualsToken(TextEditorTextSpan textSpan) + { + ConstructorWasInvoked = true; + TextSpan = textSpan; + } + + public TextEditorTextSpan TextSpan { get; } + public SyntaxKind SyntaxKind => SyntaxKind.OpenAngleBracketEqualsToken; + public bool IsFabricated { get; init; } + public bool ConstructorWasInvoked { get; } +} diff --git a/Source/Lib/TextEditor/Keymaps/Models/Defaults/TextEditorKeymapDefault.cs b/Source/Lib/TextEditor/Keymaps/Models/Defaults/TextEditorKeymapDefault.cs index d0d979819..2f2ec4214 100644 --- a/Source/Lib/TextEditor/Keymaps/Models/Defaults/TextEditorKeymapDefault.cs +++ b/Source/Lib/TextEditor/Keymaps/Models/Defaults/TextEditorKeymapDefault.cs @@ -199,6 +199,15 @@ private void AddDefaultCtrlModifiedKeymap() LayerKey = TextEditorKeymapDefaultFacts.DefaultLayer.Key }, TextEditorCommandDefaultFacts.ShowFindOverlay); + TryRegister(new KeymapArgs() + { + Key = "F", + Code = "KeyF", + CtrlKey = true, + ShiftKey = true, + LayerKey = TextEditorKeymapDefaultFacts.DefaultLayer.Key + }, TextEditorCommandDefaultFacts.PopulateSearchFindAll); + TryRegister(new KeymapArgs() { Key = "r", diff --git a/Source/Lib/TextEditor/TextEditors/Displays/Internals/FindOverlayDisplay.razor b/Source/Lib/TextEditor/TextEditors/Displays/Internals/FindOverlayDisplay.razor index 63a296c26..2847b02ff 100644 --- a/Source/Lib/TextEditor/TextEditors/Displays/Internals/FindOverlayDisplay.razor +++ b/Source/Lib/TextEditor/TextEditors/Displays/Internals/FindOverlayDisplay.razor @@ -21,7 +21,6 @@
    diff --git a/Source/Lib/TextEditor/TextEditors/Displays/Internals/FindOverlayDisplay.razor.cs b/Source/Lib/TextEditor/TextEditors/Displays/Internals/FindOverlayDisplay.razor.cs index c01a0e497..0c72a3cf5 100644 --- a/Source/Lib/TextEditor/TextEditors/Displays/Internals/FindOverlayDisplay.razor.cs +++ b/Source/Lib/TextEditor/TextEditors/Displays/Internals/FindOverlayDisplay.razor.cs @@ -32,6 +32,7 @@ public partial class FindOverlayDisplay : ComponentBase public TextEditorRenderBatchValidated? RenderBatch { get; set; } private bool _lastSeenShowFindOverlayValue = false; + private bool _lastFindOverlayValueExternallyChangedMarker = false; private string _inputValue = string.Empty; private int? _activeIndexMatchedTextSpan = null; @@ -109,6 +110,8 @@ protected override async Task OnAfterRenderAsync(bool firstRender) if (renderBatchLocal is null) return; + var becameShown = false; + if (_lastSeenShowFindOverlayValue != renderBatchLocal.ViewModel.ShowFindOverlay) { _lastSeenShowFindOverlayValue = renderBatchLocal.ViewModel.ShowFindOverlay; @@ -116,29 +119,24 @@ protected override async Task OnAfterRenderAsync(bool firstRender) // If it changes from 'false' to 'true', focus the input element if (_lastSeenShowFindOverlayValue) { + becameShown = true; + await JsRuntime.GetLuthetusCommonApi() .FocusHtmlElementById(renderBatchLocal.ViewModel.FindOverlayId) .ConfigureAwait(false); } } + + if (becameShown || + _lastFindOverlayValueExternallyChangedMarker != renderBatchLocal.ViewModel.FindOverlayValueExternallyChangedMarker) + { + _lastFindOverlayValueExternallyChangedMarker = renderBatchLocal.ViewModel.FindOverlayValueExternallyChangedMarker; + InputValue = renderBatchLocal.ViewModel.FindOverlayValue; + } await base.OnAfterRenderAsync(firstRender); } - private void HandleOnFocus() - { - var renderBatchLocal = RenderBatch; - if (renderBatchLocal is null) - return; - - // In the case where the find over value was changed, by an outside event, - // just refresh the InputValue to be sure its up to date. - // - // Example: user has a selection when using the keybind to open the find overlay, - // then the find overlay would be populated with their text selection. - InputValue = renderBatchLocal.ViewModel.FindOverlayValue; - } - private async Task HandleOnKeyDownAsync(KeyboardEventArgs keyboardEventArgs) { var renderBatchLocal = RenderBatch; diff --git a/Source/Lib/TextEditor/TextEditors/Displays/Internals/SymbolDisplay.razor b/Source/Lib/TextEditor/TextEditors/Displays/Internals/SymbolDisplay.razor index e37664239..4343a0e71 100644 --- a/Source/Lib/TextEditor/TextEditors/Displays/Internals/SymbolDisplay.razor +++ b/Source/Lib/TextEditor/TextEditors/Displays/Internals/SymbolDisplay.razor @@ -66,6 +66,19 @@ break; } + case "DiscardSymbol": + { + + + discard + + + + @symbolLocal.TextSpan.GetText() + + + break; + } case "SourceFileSymbol": { diff --git a/Source/Lib/TextEditor/TextEditors/Models/TextEditorViewModel.cs b/Source/Lib/TextEditor/TextEditors/Models/TextEditorViewModel.cs index 4ef880033..45547e8e1 100644 --- a/Source/Lib/TextEditor/TextEditors/Models/TextEditorViewModel.cs +++ b/Source/Lib/TextEditor/TextEditors/Models/TextEditorViewModel.cs @@ -166,6 +166,24 @@ public TextEditorViewModel( /// This property is what the find overlay input element binds to. /// public string FindOverlayValue { get; set; } = string.Empty; + /// + /// If the user presses the keybind to show the FindOverlayDisplay while focused on the Text Editor, + /// check if the user has a text selection. + /// + /// If they do have a text selection, then populate the FindOverlayDisplay with their selection. + /// + /// The issue arises however, how does one know whether FindOverlayValue changed due to + /// the input element itself being typed into, versus some 'background action'. + /// + /// Because the UI already will update properly if the input element itself is interacted with. + /// + /// We only need to solve the case where it was a 'background action'. + /// + /// So, if this bool toggles to a different value than what the UI last saw, + /// then the UI is to set the input element's value equal to the 'FindOverlayValue' + /// because a 'background action' modified the value. + /// + public bool FindOverlayValueExternallyChangedMarker { get; init; } /// public ViewModelUnsafeState UnsafeState { get; } /// diff --git a/Source/Tests/CompilerServices/CSharp/SmokeTests/Lexers/LexerTests.cs b/Source/Tests/CompilerServices/CSharp/SmokeTests/Lexers/LexerTests.cs index 729fa6922..056975a49 100644 --- a/Source/Tests/CompilerServices/CSharp/SmokeTests/Lexers/LexerTests.cs +++ b/Source/Tests/CompilerServices/CSharp/SmokeTests/Lexers/LexerTests.cs @@ -205,6 +205,48 @@ public void LEX_BangToken() Assert.Equal(SyntaxKind.BangToken, bangToken.SyntaxKind); } + + [Fact] + public void LEX_BangEqualsToken() + { + var resourceUri = new ResourceUri("UnitTests"); + var sourceText = "!="; + var lexerOutput = CSharpLexer.Lex(resourceUri, sourceText); + + Assert.Equal(2, lexerOutput.SyntaxTokenList.Count); + var bangEqualsToken = (BangEqualsToken)lexerOutput.SyntaxTokenList[0]; + var endOfFileToken = (EndOfFileToken)lexerOutput.SyntaxTokenList[1]; + + Assert.Equal(SyntaxKind.BangEqualsToken, bangEqualsToken.SyntaxKind); + } + + [Fact] + public void LEX_CloseAngleBracketEqualsToken() + { + var resourceUri = new ResourceUri("UnitTests"); + var sourceText = ">="; + var lexerOutput = CSharpLexer.Lex(resourceUri, sourceText); + + Assert.Equal(2, lexerOutput.SyntaxTokenList.Count); + var closeAngleBracketEqualsToken = (CloseAngleBracketEqualsToken)lexerOutput.SyntaxTokenList[0]; + var endOfFileToken = (EndOfFileToken)lexerOutput.SyntaxTokenList[1]; + + Assert.Equal(SyntaxKind.CloseAngleBracketEqualsToken, closeAngleBracketEqualsToken.SyntaxKind); + } + + [Fact] + public void LEX_OpenAngleBracketEqualsToken() + { + var resourceUri = new ResourceUri("UnitTests"); + var sourceText = "<="; + var lexerOutput = CSharpLexer.Lex(resourceUri, sourceText); + + Assert.Equal(2, lexerOutput.SyntaxTokenList.Count); + var openAngleBracketEqualsToken = (OpenAngleBracketEqualsToken)lexerOutput.SyntaxTokenList[0]; + var endOfFileToken = (EndOfFileToken)lexerOutput.SyntaxTokenList[1]; + + Assert.Equal(SyntaxKind.OpenAngleBracketEqualsToken, openAngleBracketEqualsToken.SyntaxKind); + } [Fact] public void LEX_CloseAngleBracketToken() @@ -341,6 +383,20 @@ public void LEX_EqualsEqualsToken() Assert.Equal(SyntaxKind.EqualsEqualsToken, equalsEqualsToken.SyntaxKind); } + + [Fact] + public void LEX_EqualsCloseAngleBracketToken() + { + var resourceUri = new ResourceUri("UnitTests"); + var sourceText = "=>"; + var lexerOutput = CSharpLexer.Lex(resourceUri, sourceText); + + Assert.Equal(2, lexerOutput.SyntaxTokenList.Count); + var equalsCloseAngleBracketToken = (EqualsCloseAngleBracketToken)lexerOutput.SyntaxTokenList[0]; + var endOfFileToken = (EndOfFileToken)lexerOutput.SyntaxTokenList[1]; + + Assert.Equal(SyntaxKind.EqualsCloseAngleBracketToken, equalsCloseAngleBracketToken.SyntaxKind); + } [Fact] public void LEX_EqualsToken() diff --git a/Source/Tests/CompilerServices/CSharp/SmokeTests/Parsers/ExpressionAsStatementTests.cs b/Source/Tests/CompilerServices/CSharp/SmokeTests/Parsers/ExpressionAsStatementTests.cs index 6328afb32..aee21c8e9 100644 --- a/Source/Tests/CompilerServices/CSharp/SmokeTests/Parsers/ExpressionAsStatementTests.cs +++ b/Source/Tests/CompilerServices/CSharp/SmokeTests/Parsers/ExpressionAsStatementTests.cs @@ -2246,7 +2246,7 @@ public void Array_Assignment() WriteChildrenIndentedRecursive(topCodeBlock, nameof(topCodeBlock)); var returnStatementNode = (ReturnStatementNode)topCodeBlock.GetChildList().Single(); Assert.Equal(SyntaxKind.ReturnStatementNode, returnStatementNode.SyntaxKind); - + throw new NotImplementedException(); } [Fact] @@ -2257,7 +2257,138 @@ public void Array_Access() WriteChildrenIndentedRecursive(topCodeBlock, nameof(topCodeBlock)); var returnStatementNode = (ReturnStatementNode)topCodeBlock.GetChildList().Single(); Assert.Equal(SyntaxKind.ReturnStatementNode, returnStatementNode.SyntaxKind); - + throw new NotImplementedException(); + } + + [Fact] + public void Identifier_CloseAngleBracketToken() + { + var test = new Test(@"if (aaa >) ;"); + var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; + WriteChildrenIndentedRecursive(topCodeBlock, nameof(topCodeBlock)); + var ifStatementNode = (IfStatementNode)topCodeBlock.GetChildList().Single(); + var expressionNode = ifStatementNode.ExpressionNode; + throw new NotImplementedException(); + } + + [Fact] + public void Identifier_CloseAngleBracketEqualsToken() + { + var test = new Test(@"if (aaa >=) ;"); + var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; + WriteChildrenIndentedRecursive(topCodeBlock, nameof(topCodeBlock)); + var ifStatementNode = (IfStatementNode)topCodeBlock.GetChildList().Single(); + var expressionNode = ifStatementNode.ExpressionNode; + throw new NotImplementedException(); + } + + [Fact] + public void Identifier_Pipe() + { + var test = new Test(@"if (aaa |) ;"); + var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; + WriteChildrenIndentedRecursive(topCodeBlock, nameof(topCodeBlock)); + var ifStatementNode = (IfStatementNode)topCodeBlock.GetChildList().Single(); + var expressionNode = ifStatementNode.ExpressionNode; + throw new NotImplementedException(); + } + + [Fact] + public void Identifier_PipePipe() + { + var test = new Test(@"if (aaa ||) ;"); + var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; + WriteChildrenIndentedRecursive(topCodeBlock, nameof(topCodeBlock)); + var ifStatementNode = (IfStatementNode)topCodeBlock.GetChildList().Single(); + var expressionNode = ifStatementNode.ExpressionNode; + throw new NotImplementedException(); + } + + [Fact] + public void Identifier_Ampersand() + { + var test = new Test(@"if (aaa &) ;"); + var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; + WriteChildrenIndentedRecursive(topCodeBlock, nameof(topCodeBlock)); + var ifStatementNode = (IfStatementNode)topCodeBlock.GetChildList().Single(); + var expressionNode = ifStatementNode.ExpressionNode; + throw new NotImplementedException(); + } + + [Fact] + public void Identifier_AmpersandAmpersand() + { + var test = new Test(@"if (aaa &&) ;"); + var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; + WriteChildrenIndentedRecursive(topCodeBlock, nameof(topCodeBlock)); + var ifStatementNode = (IfStatementNode)topCodeBlock.GetChildList().Single(); + var expressionNode = ifStatementNode.ExpressionNode; + throw new NotImplementedException(); + } + + [Fact] + public void Identifier_Equals() + { + var test = new Test(@"if (aaa =) ;"); + var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; + WriteChildrenIndentedRecursive(topCodeBlock, nameof(topCodeBlock)); + var ifStatementNode = (IfStatementNode)topCodeBlock.GetChildList().Single(); + var expressionNode = ifStatementNode.ExpressionNode; + throw new NotImplementedException(); + } + + [Fact] + public void Identifier_EqualsEquals() + { + var test = new Test(@"if (aaa ==) ;"); + var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; + WriteChildrenIndentedRecursive(topCodeBlock, nameof(topCodeBlock)); + var ifStatementNode = (IfStatementNode)topCodeBlock.GetChildList().Single(); + var expressionNode = ifStatementNode.ExpressionNode; + throw new NotImplementedException(); + } + + [Fact] + public void Identifier_BangEquals() + { + var test = new Test(@"if (aaa !=) ;"); + var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; + WriteChildrenIndentedRecursive(topCodeBlock, nameof(topCodeBlock)); + var ifStatementNode = (IfStatementNode)topCodeBlock.GetChildList().Single(); + var expressionNode = ifStatementNode.ExpressionNode; + throw new NotImplementedException(); + } + + /// + /// This isn't ambiguous between 'GenericParametersListingNode' and a 'less than' operator. + /// + /// Reason being, you would use the Binder to determine that the identifier prior to the '>' + /// is a variable. + /// + /// Thus, if it is a variable then the only outcome is 'less than'. + /// + /// If it is a Type then the only outcome is 'GenericParametersListingNode'. + /// + [Fact] + public void Identifier_OpenAngleBracketToken() + { + var test = new Test(@"if (aaa <) ;"); + var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; + WriteChildrenIndentedRecursive(topCodeBlock, nameof(topCodeBlock)); + var ifStatementNode = (IfStatementNode)topCodeBlock.GetChildList().Single(); + var expressionNode = ifStatementNode.ExpressionNode; + throw new NotImplementedException(); + } + + [Fact] + public void Identifier_OpenAngleBracketEqualsToken() + { + var test = new Test(@"if (aaa <=) ;"); + var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; + WriteChildrenIndentedRecursive(topCodeBlock, nameof(topCodeBlock)); + var ifStatementNode = (IfStatementNode)topCodeBlock.GetChildList().Single(); + var expressionNode = ifStatementNode.ExpressionNode; + throw new NotImplementedException(); } private void WriteChildrenIndented(ISyntaxNode node, string name = "node") diff --git a/Source/Tests/CompilerServices/CSharp/SmokeTests/Parsers/ExpressionTests.cs b/Source/Tests/CompilerServices/CSharp/SmokeTests/Parsers/ExpressionTests.cs index 684e6e0f2..59ad08d41 100644 --- a/Source/Tests/CompilerServices/CSharp/SmokeTests/Parsers/ExpressionTests.cs +++ b/Source/Tests/CompilerServices/CSharp/SmokeTests/Parsers/ExpressionTests.cs @@ -10,6 +10,7 @@ using Luthetus.CompilerServices.CSharp.LexerCase; using Luthetus.CompilerServices.CSharp.ParserCase; using Luthetus.CompilerServices.CSharp.ParserCase.Internals; +using Luthetus.CompilerServices.CSharp.BinderCase; using Luthetus.CompilerServices.CSharp.Facts; using Luthetus.CompilerServices.CSharp.CompilerServiceCase; @@ -23,7 +24,9 @@ public Test(string sourceText) { SourceText = sourceText; ResourceUri = new ResourceUri("./unitTesting.txt"); + CompilationUnit = new CSharpCompilationUnit(ResourceUri, new CSharpBinder()); CompilationUnit.LexerOutput = CSharpLexer.Lex(ResourceUri, SourceText); + CompilationUnit.BinderSession = (CSharpBinderSession)CompilationUnit.Binder.StartBinderSession(ResourceUri); CSharpParser.Parse(CompilationUnit); } @@ -1173,14 +1176,14 @@ public void CollectionInitializationNode_WithParenthesis() [Fact] public void LambdaFunction_Expression_NoParameter() { - var test = new Test("() => \"Abc\";"); + var test = new Test("(() => \"Abc\");"); var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; - var lambdaExpressionNode = (LambdaExpressionNode)topCodeBlock.GetChildList().Single(); + var parenthesizedExpressionNode = (ParenthesizedExpressionNode)topCodeBlock.GetChildList().Single(); + var lambdaExpressionNode = (LambdaExpressionNode)parenthesizedExpressionNode.InnerExpression; + Assert.True(lambdaExpressionNode.CodeBlockNodeIsExpression); Assert.Empty(lambdaExpressionNode.VariableDeclarationNodeList); - - throw new NotImplementedException("See ExpressionAsStatementTests"); } [Fact] @@ -1345,8 +1348,8 @@ So remember the general type clause matching not just an identifier. Assert.Equal(TypeFacts.Empty.ToTypeClause(), parameter.TypeClauseNode); Assert.Equal("x", parameter.IdentifierToken.TextSpan.GetText()); Assert.Equal(VariableKind.Local, parameter.VariableKind); - - throw new NotImplementedException("See ExpressionAsStatementTests"); + + } [Fact] @@ -1377,47 +1380,39 @@ public void LambdaFunction_Expression_ManyParameter_Async() Assert.Equal("index", parameter.IdentifierToken.TextSpan.GetText()); Assert.Equal(VariableKind.Local, parameter.VariableKind); } - - throw new NotImplementedException("See ExpressionAsStatementTests"); } [Fact] public void LambdaFunction_CodeBlock_NoParameter_Async() { - var test = new Test("async () => { WriteLine(\"Abc\"); return \"Cba\"; };"); + var test = new Test("(async () => { WriteLine(\"Abc\"); return \"Cba\"; });"); var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; var lambdaExpressionNode = (LambdaExpressionNode)topCodeBlock.GetChildList().Single(); throw new NotImplementedException(); - - throw new NotImplementedException("See ExpressionAsStatementTests"); } [Fact] public void LambdaFunction_CodeBlock_SingleParameter_Async() { - var test = new Test("async x => { return \"Abc\"; };"); + var test = new Test("(async x => { return \"Abc\"; });"); var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; var lambdaExpressionNode = (LambdaExpressionNode)topCodeBlock.GetChildList().Single(); throw new NotImplementedException(); - - throw new NotImplementedException("See ExpressionAsStatementTests"); } [Fact] public void LambdaFunction_CodeBlock_ManyParameter() { - var test = new Test("(x, index) => { return \"Abc\"; };"); + var test = new Test("((x, index) => { return \"Abc\"; });"); var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; var lambdaExpressionNode = (LambdaExpressionNode)topCodeBlock.GetChildList().Single(); throw new NotImplementedException(); - - throw new NotImplementedException("See ExpressionAsStatementTests"); } [Fact] diff --git a/Source/Tests/CompilerServices/CSharp/SmokeTests/Parsers/ScopeTests.cs b/Source/Tests/CompilerServices/CSharp/SmokeTests/Parsers/ScopeTests.cs index 728bec331..95aac0248 100644 --- a/Source/Tests/CompilerServices/CSharp/SmokeTests/Parsers/ScopeTests.cs +++ b/Source/Tests/CompilerServices/CSharp/SmokeTests/Parsers/ScopeTests.cs @@ -10,6 +10,7 @@ using Luthetus.CompilerServices.CSharp.LexerCase; using Luthetus.CompilerServices.CSharp.ParserCase; using Luthetus.CompilerServices.CSharp.ParserCase.Internals; +using Luthetus.CompilerServices.CSharp.BinderCase; using Luthetus.CompilerServices.CSharp.Facts; using Luthetus.CompilerServices.CSharp.CompilerServiceCase; @@ -42,12 +43,15 @@ public Test(string sourceText) { SourceText = sourceText; ResourceUri = new ResourceUri("./unitTesting.txt"); + CompilationUnit = new CSharpCompilationUnit(ResourceUri, new CSharpBinder()); CompilationUnit.LexerOutput = CSharpLexer.Lex(ResourceUri, SourceText); + CompilationUnit.BinderSession = (CSharpBinderSession)CompilationUnit.Binder.StartBinderSession(ResourceUri); CSharpParser.Parse(CompilationUnit); } public string SourceText { get; set; } public ResourceUri ResourceUri { get; set; } + public CSharpLexerOutput LexerOutput { get; set; } public IBinder Binder => CompilationUnit.Binder; public CSharpCompilationUnit CompilationUnit { get; set; } } diff --git a/Source/Tests/CompilerServices/CSharp/SmokeTests/Parsers/StatementTests.cs b/Source/Tests/CompilerServices/CSharp/SmokeTests/Parsers/StatementTests.cs index 7c5af6355..04006ba8d 100644 --- a/Source/Tests/CompilerServices/CSharp/SmokeTests/Parsers/StatementTests.cs +++ b/Source/Tests/CompilerServices/CSharp/SmokeTests/Parsers/StatementTests.cs @@ -10,6 +10,7 @@ using Luthetus.CompilerServices.CSharp.LexerCase; using Luthetus.CompilerServices.CSharp.ParserCase; using Luthetus.CompilerServices.CSharp.ParserCase.Internals; +using Luthetus.CompilerServices.CSharp.BinderCase; using Luthetus.CompilerServices.CSharp.Facts; using Luthetus.CompilerServices.CSharp.CompilerServiceCase; @@ -23,12 +24,15 @@ public Test(string sourceText) { SourceText = sourceText; ResourceUri = new ResourceUri("./unitTesting.txt"); + CompilationUnit = new CSharpCompilationUnit(ResourceUri, new CSharpBinder()); CompilationUnit.LexerOutput = CSharpLexer.Lex(ResourceUri, SourceText); + CompilationUnit.BinderSession = (CSharpBinderSession)CompilationUnit.Binder.StartBinderSession(ResourceUri); CSharpParser.Parse(CompilationUnit); } public string SourceText { get; set; } public ResourceUri ResourceUri { get; set; } + public CSharpLexerOutput LexerOutput { get; set; } public IBinder Binder => CompilationUnit.Binder; public CSharpCompilationUnit CompilationUnit { get; set; } } @@ -312,6 +316,20 @@ public void VariableDeclarationNodeAndAssignment_Var_Test() // Assert.Equal(SyntaxKind.WhileStatementNode, whileStatementNode.SyntaxKind); } + [Fact] + public void VariableDeclarationNodeAndAssignment_Var_Test_DetermineImplicitType() + { + var test = new Test(@"var aaa = 2;"); + + var topCodeBlock = test.CompilationUnit.RootCodeBlockNode; + WriteChildrenIndentedRecursive(topCodeBlock); + + var variableDeclarationNode = (VariableDeclarationNode)topCodeBlock.GetChildList()[0]; + var variableAssignmentNode = (VariableAssignmentExpressionNode)topCodeBlock.GetChildList()[1]; + + // Assert.Equal(SyntaxKind.WhileStatementNode, whileStatementNode.SyntaxKind); + } + [Fact] public void VariableDeclarationNodeAndAssignment_IdentifierToken_Test() {