internal static IList <XSharpToken> GetTokensUnderCursor(XSharpSearchLocation location, out CompletionState state) { var tokens = GetTokenList(location, out state, true, true).Where((t) => t.Channel == XSharpLexer.DefaultTokenChannel).ToList(); // Find "current" token if (tokens.Count > 0) { var tokenUnderCursor = tokens.Count - 1; for (int i = tokens.Count - 1; i >= 0; i--) { var token = tokens[i]; if (token.StartIndex <= location.Position && token.StopIndex >= location.Position) { tokenUnderCursor = i; break; } } var selectedToken = tokens[tokenUnderCursor]; var nextToken = tokenUnderCursor < tokens.Count - 1 ? tokens[tokenUnderCursor + 1] : null; bool done = false; switch (selectedToken.Type) { case XSharpLexer.NAMEOF: case XSharpLexer.TYPEOF: case XSharpLexer.SIZEOF: case XSharpLexer.SELF: case XSharpLexer.SUPER: if (nextToken != null && nextToken.Type == XSharpLexer.LPAREN) { return(tokens); } break; default: if (XSharpLexer.IsKeyword(selectedToken.Type)) { tokens.Clear(); tokens.Add(selectedToken); return(tokens); } break; } // When we are not on a Keyword then we need to walk back in the tokenlist to see // if we can evaluate the expression // This could be: // System.String.Compare() // static method cal or method call // SomeVar:MethodCall() // method call // Left(...) // function call // SomeId // local, global etc // SomeType.Id // Static property or normal property // SomeVar:Id // Instance field or property // If the token list contains with a RCURLY, RBRKT or RPAREN // Then strip everything until the matching LCURLY, LBRKT or LPAREN is found var list = new XSharpTokenList(tokens); tokens = new List <XSharpToken>(); while (!list.Eoi()) { var token = list.ConsumeAndGet(); switch (token.Type) { case XSharpLexer.LCURLY: tokens.Add(token); if (list.Contains(XSharpLexer.RCURLY)) { // this may return false when the RCURLY belongs to another LCURLY if (list.ConsumeUntilEndToken(XSharpLexer.RCURLY, out var endToken)) { tokens.Add(endToken); } } break; case XSharpLexer.LPAREN: tokens.Add(token); if (list.Contains(XSharpLexer.RPAREN)) { // this may return false when the RPAREN belongs to another LPAREN if (list.ConsumeUntilEndToken(XSharpLexer.RPAREN, out var endToken)) { tokens.Add(endToken); } } break; case XSharpLexer.LBRKT: tokens.Add(token); if (list.Contains(XSharpLexer.RBRKT)) { // this may return false when the RBRKT belongs to another LBRKT if (list.ConsumeUntilEndToken(XSharpLexer.RBRKT, out var endToken)) { tokens.Add(endToken); } } break; case XSharpLexer.DOT: case XSharpLexer.COLON: case XSharpLexer.SELF: case XSharpLexer.SUPER: tokens.Add(token); break; default: tokens.Add(token); if (XSharpLexer.IsOperator(token.Type)) { done = true; } if (token.Type == XSharpLexer.VAR) { done = true; } else if (XSharpLexer.IsKeyword(token.Type) && !XSharpLexer.IsPositionalKeyword(token.Type) ) { done = true; } break; } } // now result has the list of tokens starting with the cursor // we only keep: // ID, DOT, COLON, LPAREN, LBRKT, RBRKT // when we detect another token we truncate the list there if (tokens.Count > 0) { var lastType = tokens[0].Type; for (int i = tokenUnderCursor + 1; i < tokens.Count && !done; i++) { var token = tokens[i]; switch (token.Type) { case XSharpLexer.ID: case XSharpLexer.DOT: case XSharpLexer.COLON: case XSharpLexer.LPAREN: case XSharpLexer.LCURLY: case XSharpLexer.LBRKT: lastType = tokens[i].Type; break; case XSharpLexer.LT: int gtPos = findTokenInList(tokens, i + 1, XSharpLexer.GT); if (lastType == XSharpLexer.ID && gtPos > 0) { gtPos += 1; tokens.RemoveRange(gtPos, tokens.Count - gtPos); done = true; break; } else { goto default; } default: tokens.RemoveRange(i, tokens.Count - i); done = true; break; } } } } // check for extra lparen, lcurly at the end int count = tokens.Count; if (count > 2 && count < tokens.Count - 2) { if (tokens[count - 2].Type == XSharpLexer.LPAREN) { switch (tokens[count - 1].Type) { case XSharpLexer.LPAREN: case XSharpLexer.LCURLY: tokens.RemoveAt(count - 1); break; } } } return(tokens); }