예제 #1
0
 public override string GetDataTipText(int line, int col, out TextSpan span)
 {
     CMakeParsing.TokenData tokenData;
     if (_lines != null &&
         CMakeParsing.ParseForToken(_lines, line, col, out tokenData) &&
         !tokenData.InParens)
     {
         if (tokenData.TokenInfo.Token == (int)CMakeToken.Keyword)
         {
             // Get a Quick Info tip for the command at the cursor.
             span = tokenData.TokenInfo.ToTextSpan(line);
             string         lineText  = _lines.ToList()[line];
             string         tokenText = lineText.ExtractToken(tokenData.TokenInfo);
             CMakeCommandId id        = CMakeKeywords.GetCommandId(tokenText);
             if (CMakeSubcommandMethods.HasSubcommands(id))
             {
                 // Parse to get the subcommand, if there is one.
                 CMakeParsing.ParameterInfoResult result =
                     CMakeParsing.ParseForParameterInfo(_lines, line, -1, true);
                 return(CMakeSubcommandMethods.GetSubcommandQuickInfoTip(id,
                                                                         result.SubcommandName));
             }
             return(CMakeMethods.GetCommandQuickInfoTip(id));
         }
         else if (tokenData.TokenInfo.Token == (int)CMakeToken.Identifier)
         {
             // Get a Quick Info tip for the function called at the cursor, if
             // there is one.
             string        lineText   = _lines.ToList()[line];
             string        tokenText  = lineText.ExtractToken(tokenData.TokenInfo);
             List <string> parameters = CMakeParsing.ParseForParameterNames(_lines,
                                                                            tokenText);
             if (parameters != null)
             {
                 span = tokenData.TokenInfo.ToTextSpan(line);
                 return(string.Format("{0}({1})", tokenText,
                                      string.Join(" ", parameters)));
             }
         }
     }
     span = new TextSpan();
     return(null);
 }
예제 #2
0
        public void TestQuickInfoTips()
        {
            // Test ordinary commands.
            Assert.AreEqual("if(expression)",
                            CMakeMethods.GetCommandQuickInfoTip(CMakeCommandId.If));
            Assert.AreEqual("set(variable value)",
                            CMakeMethods.GetCommandQuickInfoTip(CMakeCommandId.Set));
            Assert.IsNull(CMakeMethods.GetCommandQuickInfoTip(
                              CMakeCommandId.AddCustomCommand));

            // Test subcommands.
            Assert.AreEqual("cmake_policy(PUSH)",
                            CMakeSubcommandMethods.GetSubcommandQuickInfoTip(
                                CMakeCommandId.CMakePolicy, "PUSH"));
            Assert.AreEqual("export(PACKAGE name)",
                            CMakeSubcommandMethods.GetSubcommandQuickInfoTip(
                                CMakeCommandId.Export, "PACKAGE"));
            Assert.AreEqual("file(READ filename variable)",
                            CMakeSubcommandMethods.GetSubcommandQuickInfoTip(
                                CMakeCommandId.File, "READ"));
            Assert.IsNull(CMakeSubcommandMethods.GetSubcommandQuickInfoTip(
                              CMakeCommandId.Install, "CODE"));
        }
예제 #3
0
        public override AuthoringScope ParseSource(ParseRequest req)
        {
            CMakeAuthoringScope scope = new CMakeAuthoringScope();

            if (!CMakeSource.IsCMakeFile(req.FileName))
            {
                // Don't do IntelliSense parsing for ordinary text files.
                return(scope);
            }
            CMakeSource source = (CMakeSource)GetSource(req.FileName);

            if (req.Sink.HiddenRegions)
            {
                req.Sink.ProcessHiddenRegions = true;
                List <TextSpan> regions = CMakeParsing.ParseForFunctionBodies(
                    source.GetLines());
                foreach (TextSpan textSpan in regions)
                {
                    req.Sink.AddHiddenRegion(textSpan);
                }
            }
            if (req.Sink.BraceMatching)
            {
                List <CMakeParsing.SpanPair> pairs = null;
                switch ((CMakeToken)req.TokenInfo.Token)
                {
                case CMakeToken.OpenParen:
                case CMakeToken.CloseParen:
                    pairs = CMakeParsing.ParseForParens(source.GetLines());
                    break;

                case CMakeToken.VariableStart:
                case CMakeToken.VariableStartEnv:
                case CMakeToken.VariableStartCache:
                case CMakeToken.VariableStartSetEnv:
                case CMakeToken.VariableEnd:
                    pairs = CMakeParsing.ParseForVariableBraces(source.GetLines(),
                                                                req.Line);
                    break;

                case CMakeToken.GeneratorStart:
                case CMakeToken.GeneratorEnd:
                    pairs = CMakeParsing.ParseForGeneratorBraces(source.GetLines(),
                                                                 req.Line);
                    break;
                }
                if (pairs != null)
                {
                    foreach (CMakeParsing.SpanPair pair in pairs)
                    {
                        req.Sink.MatchPair(pair.First, pair.Second, 0);
                    }
                }
            }
            if (req.Reason == ParseReason.MemberSelect ||
                req.Reason == ParseReason.MemberSelectAndHighlightBraces ||
                req.Reason == ParseReason.CompleteWord)
            {
                // Set an appropriate declarations object depending on the token that
                // triggered member selection.
                CMakeToken token = (CMakeToken)req.TokenInfo.Token;
                if (token == CMakeToken.String)
                {
                    // If the token is a string and the user has began to reference a
                    // variable inside the string, treat the string as if it was the
                    // appropriate type of variable start token and display member
                    // selection for variables.
                    string line      = source.GetLine(req.Line);
                    string tokenText = line.ExtractToken(req.TokenInfo);
                    if (tokenText.EndsWith("${"))
                    {
                        token = CMakeToken.VariableStart;
                    }
                    else if (tokenText.EndsWith("$ENV{"))
                    {
                        token = CMakeToken.VariableStartEnv;
                    }
                    else if (tokenText.EndsWith("$CACHE{"))
                    {
                        token = CMakeToken.VariableStartCache;
                    }
                }
                if (token == CMakeToken.VariableStart)
                {
                    List <string> vars = CMakeParsing.ParseForVariables(
                        source.GetLines(), req.Line);
                    CMakeVariableDeclarations decls = new CMakeVariableDeclarations(vars,
                                                                                    CMakeVariableType.Variable);
                    decls.AddItems(source.GetIncludeCacheVariables(),
                                   CMakeItemDeclarations.ItemType.Variable);
                    string functionName = CMakeParsing.ParseForCurrentFunction(
                        source.GetLines(), req.Line);
                    if (functionName != null)
                    {
                        List <string> paramNames = CMakeParsing.ParseForParameterNames(
                            source.GetLines(), functionName);
                        paramNames.Add("ARGN");
                        decls.AddItems(paramNames,
                                       CMakeItemDeclarations.ItemType.Variable);
                    }
                    scope.SetDeclarations(decls);
                }
                else if (token == CMakeToken.VariableStartEnv)
                {
                    List <string> vars = CMakeParsing.ParseForEnvVariables(
                        source.GetLines());
                    CMakeVariableDeclarations decls = new CMakeVariableDeclarations(vars,
                                                                                    CMakeVariableType.EnvVariable);
                    decls.AddItems(source.GetIncludeCacheEnvVariables(),
                                   CMakeItemDeclarations.ItemType.Variable);
                    scope.SetDeclarations(decls);
                }
                else if (token == CMakeToken.VariableStartCache)
                {
                    List <string> vars = CMakeParsing.ParseForCacheVariables(
                        source.GetLines());
                    CMakeVariableDeclarations decls = new CMakeVariableDeclarations(vars,
                                                                                    CMakeVariableType.CacheVariable);
                    decls.AddItems(source.GetIncludeCacheCacheVariables(),
                                   CMakeItemDeclarations.ItemType.Variable);
                    scope.SetDeclarations(decls);
                }
                else if (token == CMakeToken.Identifier)
                {
                    CMakeParsing.TokenData tokenData;
                    CMakeParsing.ParseForToken(source.GetLines(), req.Line,
                                               req.TokenInfo.StartIndex, out tokenData);
                    if (!tokenData.InParens)
                    {
                        CMakeItemDeclarations decls    = new CMakeItemDeclarations();
                        IEnumerable <string>  commands = CMakeKeywords.GetAllCommands(
                            CMakePackage.Instance.CMakeOptionPage.ShowDeprecated);
                        if (!CMakePackage.Instance.CMakeOptionPage.CommandsLower)
                        {
                            commands = commands.Select(x => x.ToUpper());
                        }
                        decls.AddItems(commands, CMakeItemDeclarations.ItemType.Command);
                        decls.AddItems(
                            CMakeParsing.ParseForFunctionNames(source.GetLines(), false),
                            CMakeItemDeclarations.ItemType.Function);
                        decls.AddItems(
                            CMakeParsing.ParseForFunctionNames(source.GetLines(), true),
                            CMakeItemDeclarations.ItemType.Macro);
                        decls.AddItems(source.GetIncludeCacheFunctions(),
                                       CMakeItemDeclarations.ItemType.Function);
                        decls.AddItems(source.GetIncludeCacheMacros(),
                                       CMakeItemDeclarations.ItemType.Macro);
                        scope.SetDeclarations(decls);
                    }
                    else
                    {
                        Declarations decls = CMakeDeclarationsFactory.CreateDeclarations(
                            tokenData.Command, req, source,
                            tokenData.ParameterIndex > 0 ? tokenData.PriorParameters : null);
                        scope.SetDeclarations(decls);
                    }
                }
                else if (token == CMakeToken.OpenParen)
                {
                    CMakeCommandId id = CMakeParsing.ParseForTriggerCommandId(
                        source.GetLines(), req.Line, req.TokenInfo.StartIndex);
                    Declarations decls = CMakeDeclarationsFactory.CreateDeclarations(
                        id, req, source);
                    scope.SetDeclarations(decls);
                }
                else if (token == CMakeToken.WhiteSpace)
                {
                    CMakeParsing.TokenData tokenData;
                    CMakeParsing.ParseForToken(source.GetLines(), req.Line,
                                               req.TokenInfo.StartIndex, out tokenData);
                    Declarations decls = CMakeDeclarationsFactory.CreateDeclarations(
                        tokenData.Command, req, source,
                        tokenData.ParameterIndex > 0 ? tokenData.PriorParameters : null);
                    scope.SetDeclarations(decls);
                }
                else if (token == CMakeToken.GeneratorStart)
                {
                    scope.SetDeclarations(new CMakeGeneratorDeclarations());
                }
            }
            else if (req.Reason == ParseReason.MethodTip)
            {
                CMakeParsing.ParameterInfoResult result =
                    CMakeParsing.ParseForParameterInfo(source.GetLines(), req.Line,
                                                       req.TokenInfo.EndIndex);
                if (result.CommandName != null && result.CommandSpan.HasValue)
                {
                    if (result.SubcommandName == null)
                    {
                        req.Sink.StartName(result.CommandSpan.Value, result.CommandName);
                    }
                    else
                    {
                        req.Sink.StartName(result.CommandSpan.Value,
                                           result.CommandSpan + "(" + result.SubcommandName);
                    }
                    if (result.BeginSpan.HasValue)
                    {
                        req.Sink.StartParameters(result.BeginSpan.Value);
                    }
                    foreach (TextSpan span in result.SeparatorSpans)
                    {
                        req.Sink.NextParameter(span);
                    }
                    if (result.EndSpan.HasValue)
                    {
                        req.Sink.EndParameters(result.EndSpan.Value);
                    }
                    CMakeCommandId id = CMakeKeywords.GetCommandId(result.CommandName);
                    if (id == CMakeCommandId.Unspecified)
                    {
                        // If it's a user-defined function or macro, parse to try to find
                        // its parameters.
                        List <string> parameters = CMakeParsing.ParseForParameterNames(
                            source.GetLines(), result.CommandName);
                        if (parameters == null)
                        {
                            parameters = source.GetParametersFromIncludeCache(
                                result.CommandName);
                        }
                        if (parameters != null)
                        {
                            scope.SetMethods(new CMakeUserMethods(result.CommandName,
                                                                  parameters));
                        }
                    }
                    else
                    {
                        scope.SetMethods(CMakeMethods.GetCommandParameters(id,
                                                                           result.SubcommandName));
                    }
                }
            }
            else if (req.Reason == ParseReason.Goto)
            {
                scope.SetLines(source.GetLines());
                scope.SetFileName(req.FileName);
            }
            else if (req.Reason == ParseReason.QuickInfo)
            {
                scope.SetLines(source.GetLines());
            }
            else if (req.Reason == ParseReason.Check)
            {
                foreach (ParseForErrorMethod method in _parseForErrorMethods)
                {
                    List <CMakeErrorInfo> info = method(source.GetLines());
                    foreach (CMakeErrorInfo item in info)
                    {
                        CMakeError err = item.ErrorCode;
                        if (_errorStrings.ContainsKey(err) &&
                            (!_enabledMethods.ContainsKey(err) || _enabledMethods[err]()))
                        {
                            req.Sink.AddError(req.FileName, _errorStrings[err], item.Span,
                                              item.Warning ? Severity.Warning : Severity.Error);
                        }
                    }
                }
                if (CMakePackage.Instance.CMakeOptionPage.ParseIncludedFiles)
                {
                    source.BuildIncludeCache(source.GetLines());
                    source.UpdateIncludeCache();
                    source.PruneIncludeCache();
                }
                else
                {
                    source.ClearIncludeCache();
                }
            }
            return(scope);
        }
예제 #4
0
        public bool ScanTokenAndProvideInfoAboutIt(TokenInfo tokenInfo, ref int state)
        {
            if (_textFile)
            {
                // Don't perform syntax highlighting if the file is an ordinary text
                // file.
                return(false);
            }

            if (GetStringFlag(state) && _offset < _source.Length)
            {
                // If the line begins inside a string token, begin by scanning the rest
                // of the string.
                ScanString(tokenInfo, ref state, false);
                _scannedNonWhitespace = true;
                _lastWhitespace       = false;
                return(true);
            }
            bool bracketComment  = GetBracketCommentFlag(state);
            bool bracketArgument = GetBracketArgumentFlag(state);

            if ((bracketComment || bracketArgument) && _offset < _source.Length)
            {
                // If the line begins inside a bracket comment token, begin by scanning
                // the rest of the bracket comment.
                ScanBracketCommentOrArgument(tokenInfo, bracketComment, ref state);
                _lastWhitespace = true;
                return(true);
            }

            bool originalScannedNonWhitespace = _scannedNonWhitespace;
            bool originalLastWhitespace       = _lastWhitespace;
            int  originalVariableDepth        = GetVariableDepth(state);

            SetVariableDepth(ref state, 0);
            bool noSeparator = GetNoSeparatorFlag(state);

            SetNoSeparatorFlag(ref state, false);
            tokenInfo.Trigger = TokenTriggers.None;
            _lastWhitespace   = false;
            while (_offset < _source.Length)
            {
                if (char.IsWhiteSpace(_source[_offset]))
                {
                    // Scan a whitespace token.
                    tokenInfo.StartIndex = _offset;
                    while (_offset < _source.Length &&
                           char.IsWhiteSpace(_source[_offset]))
                    {
                        _offset++;
                    }
                    tokenInfo.EndIndex = _offset - 1;
                    tokenInfo.Color    = TokenColor.Text;
                    tokenInfo.Token    = (int)CMakeToken.WhiteSpace;
                    CMakeCommandId id = GetLastCommand(state);
                    if (InsideParens(state))
                    {
                        if (!noSeparator)
                        {
                            if (CMakeSubcommandMethods.HasSubcommands(id))
                            {
                                // The first whitespace token after a subcommand marks
                                // the beginning of the parameters.  The remaining
                                // whitespace parameters separate consecutive parameters.
                                if (GetSubcommandParmsFlag(state))
                                {
                                    if (GetNeedSubcommandFlag(state))
                                    {
                                        SetNeedSubcommandFlag(ref state, false);
                                        tokenInfo.Trigger = TokenTriggers.ParameterStart;
                                    }
                                    else if (!originalLastWhitespace)
                                    {
                                        tokenInfo.Trigger = TokenTriggers.ParameterNext;
                                    }
                                }
                            }
                            else if (id == CMakeCommandId.Unspecified ||
                                     GetSeparatorCount(state) > 0)
                            {
                                if (!originalLastWhitespace)
                                {
                                    SetSeparatorCount(ref state,
                                                      GetSeparatorCount(state) - 1);
                                    tokenInfo.Trigger = TokenTriggers.ParameterNext;
                                }
                            }
                        }
                        if (CMakeKeywords.TriggersMemberSelection(id) ||
                            CMakeKeywords.TriggersMemberSelectionOnWhiteSpace(id))
                        {
                            tokenInfo.Trigger |= TokenTriggers.MemberSelect;
                        }
                    }
                    SetNoSeparatorFlag(ref state, true);
                    _lastWhitespace = true;
                    return(true);
                }
                else if (_source[_offset] == '#')
                {
                    // Check if it's a bracket comment.  If so, handle it specially.
                    if (_offset + 1 < _source.Length && _source[_offset + 1] == '[')
                    {
                        int i = _offset + 2;
                        while (i < _source.Length && _source[i] == '=')
                        {
                            i++;
                        }
                        if (i < _source.Length && _source[i] == '[')
                        {
                            SetBracketCommentFlag(ref state, true);
                            ScanBracketCommentOrArgument(tokenInfo, true, ref state);
                            _lastWhitespace = true;
                            return(true);
                        }
                    }

                    // Scan a comment token.
                    tokenInfo.StartIndex = _offset;
                    int endPos = _source.IndexOf('\n', _offset);
                    if (endPos > _offset)
                    {
                        while (endPos < _source.Length - 1 &&
                               (_source[endPos + 1] == '\r' || _source[endPos + 1] == '\n'))
                        {
                            endPos++;
                        }
                    }
                    else
                    {
                        endPos = _source.Length - 1;
                    }
                    tokenInfo.EndIndex = endPos;
                    tokenInfo.Color    = TokenColor.Comment;
                    tokenInfo.Token    = (int)CMakeToken.Comment;
                    _offset            = endPos + 1;
                    SetNoSeparatorFlag(ref state, noSeparator);
                    return(true);
                }

                // If we haven't returned by this point, the token is something other
                // than whitespace.  Therefore, set the flag indicating that a
                // non-whitespace token has been scanned on the current line so that
                // any additional identifier characters at the end won't trigger member
                // selection.  If you add any additional token, ensure that you handle
                // them after this point.
                _scannedNonWhitespace = true;

                if (_source[_offset] == '"')
                {
                    // Scan a string token.
                    ScanString(tokenInfo, ref state, true);
                    return(true);
                }
                else if (_source[_offset] == '(')
                {
                    // Scan an opening parenthesis.
                    if (!InsideParens(state))
                    {
                        CMakeCommandId id = GetLastCommand(state);
                        if (CMakeKeywords.TriggersMemberSelection(id))
                        {
                            tokenInfo.Trigger |= TokenTriggers.MemberSelect;
                            SetNeedSubcommandFlag(ref state, true);
                            SetNoSeparatorFlag(ref state, true);
                        }
                        else
                        {
                            tokenInfo.Trigger |= TokenTriggers.ParameterStart;
                            SetNoSeparatorFlag(ref state, true);
                        }
                    }
                    IncParenDepth(ref state);
                    tokenInfo.StartIndex = _offset;
                    tokenInfo.EndIndex   = _offset;
                    tokenInfo.Color      = TokenColor.Text;
                    tokenInfo.Token      = (int)CMakeToken.OpenParen;
                    tokenInfo.Trigger   |= TokenTriggers.MatchBraces;
                    _offset++;
                    return(true);
                }
                else if (_source[_offset] == ')')
                {
                    // Scan a closing parenthesis.
                    DecParenDepth(ref state);
                    if (GetParenDepth(state) == 0)
                    {
                        SetLastCommand(ref state, CMakeCommandId.Unspecified);
                        SetSeparatorCount(ref state, 0);
                        tokenInfo.Trigger = TokenTriggers.ParameterEnd;
                    }
                    tokenInfo.StartIndex = _offset;
                    tokenInfo.EndIndex   = _offset;
                    tokenInfo.Color      = TokenColor.Text;
                    tokenInfo.Token      = (int)CMakeToken.CloseParen;
                    tokenInfo.Trigger   |= TokenTriggers.MatchBraces;
                    _offset++;
                    return(true);
                }
                else if (char.IsLetter(_source[_offset]) || _source[_offset] == '_' ||
                         (originalVariableDepth == 0 && _source[_offset] == '\\'))
                {
                    // Scan a keyword, identifier, or file name token.
                    bool isFileName = false;
                    bool isNumeric  = false;
                    tokenInfo.StartIndex = _offset;
                    if (_source[_offset] == '\\' && _offset != _source.Length - 1)
                    {
                        // If the identifier starts with an escape sequence, skip over it.
                        _offset++;
                        isFileName = true;
                    }
                    while (_offset < _source.Length - 1)
                    {
                        char ch = _source[_offset + 1];
                        if (ch == '-')
                        {
                            // Variable names may contain hyphens but function names
                            // can't.  There classify an identifier with a hyphen in it
                            // as a numeric identifier.
                            isNumeric = true;
                        }
                        else if (originalVariableDepth == 0 && ScanFileNameChar())
                        {
                            isFileName = true;
                        }
                        else if (!char.IsLetterOrDigit(ch) && ch != '_')
                        {
                            break;
                        }
                        _offset++;
                    }
                    tokenInfo.EndIndex = _offset;
                    _offset++;

                    CMakeCommandId id     = GetLastCommand(state);
                    string         substr = _source.ExtractToken(tokenInfo);
                    if (originalVariableDepth != 0)
                    {
                        // If we're inside curly braces following a dollar sign, treat
                        // the identifier as a variable.
                        tokenInfo.Color = TokenColor.Identifier;
                        tokenInfo.Token = (int)CMakeToken.Variable;
                        SetVariableDepth(ref state, originalVariableDepth);
                    }
                    else if ((id == CMakeCommandId.Set || id == CMakeCommandId.Unset) &&
                             substr.StartsWith("ENV{"))
                    {
                        // Inside a SET or UNSET command, ENV{ indicates an environment
                        // variable.  This token is case-sensitive.
                        SetVariableDepth(ref state, originalVariableDepth + 1);
                        tokenInfo.EndIndex = tokenInfo.StartIndex + 3;
                        tokenInfo.Color    = TokenColor.Identifier;
                        tokenInfo.Token    = (int)CMakeToken.VariableStartSetEnv;
                        _offset            = tokenInfo.EndIndex + 1;
                    }
                    else if (isNumeric)
                    {
                        tokenInfo.Color = TokenColor.Identifier;
                        tokenInfo.Token = (int)CMakeToken.NumericIdentifier;
                    }
                    else if (isFileName)
                    {
                        // If we found characters that aren't valid in an identifier,
                        // treat the token as a file name.
                        tokenInfo.Color = TokenColor.Identifier;
                        tokenInfo.Token = (int)CMakeToken.FileName;
                    }
                    else
                    {
                        // Check whether the string is a keyword or not.
                        string tokenText = _source.ExtractToken(tokenInfo);
                        bool   isKeyword = false;
                        if (!InsideParens(state))
                        {
                            isKeyword = CMakeKeywords.IsCommand(tokenText);
                            id        = CMakeKeywords.GetCommandId(tokenText);
                            SetLastCommand(ref state, id);
                            SetSeparatorCount(ref state,
                                              CMakeMethods.GetParameterCount(id));
                            int count = GetSeparatorCount(state);
                            if (count > 0)
                            {
                                SetSeparatorCount(ref state, count - 1);
                            }
                        }
                        else
                        {
                            isKeyword = CMakeKeywords.IsKeyword(id, tokenText);
                            if (isKeyword)
                            {
                                SetSubcommandParmsFlag(ref state,
                                                       CMakeSubcommandMethods.HasSubcommandParameters(
                                                           id, tokenText));
                            }
                        }
                        tokenInfo.Color = isKeyword ? TokenColor.Keyword :
                                          TokenColor.Identifier;
                        tokenInfo.Token = isKeyword ? (int)CMakeToken.Keyword :
                                          (int)CMakeToken.Identifier;
                    }
                    if (tokenInfo.StartIndex == tokenInfo.EndIndex)
                    {
                        if (!InsideParens(state) && !originalScannedNonWhitespace)
                        {
                            // Trigger member selection if we're not inside parentheses.
                            tokenInfo.Trigger |= TokenTriggers.MemberSelect;
                        }
                        else if (!originalScannedNonWhitespace || originalLastWhitespace)
                        {
                            // Always trigger member selection in response to certain
                            // commands.
                            if (CMakeKeywords.TriggersMemberSelection(id) ||
                                CMakeKeywords.TriggersMemberSelectionOnWhiteSpace(id))
                            {
                                tokenInfo.Trigger |= TokenTriggers.MemberSelect;
                            }
                        }
                    }
                    return(true);
                }
                else if (char.IsDigit(_source[_offset]) || _source[_offset] == '-')
                {
                    // Variable names can start with numbers or hyphens in CMake, but
                    // function names can't.  We'll call these tokens "numeric
                    // identifiers" here and treat them accordingly when parsing.
                    tokenInfo.StartIndex = _offset;
                    bool isFileName = false;
                    while (_offset < _source.Length - 1)
                    {
                        char ch = _source[_offset + 1];
                        if (originalVariableDepth == 0 && ScanFileNameChar())
                        {
                            isFileName = true;
                        }
                        else if (!char.IsLetterOrDigit(ch) && ch != '_')
                        {
                            break;
                        }
                        _offset++;
                    }
                    tokenInfo.EndIndex = _offset;
                    _offset++;
                    tokenInfo.Color = TokenColor.Identifier;
                    if (originalVariableDepth != 0)
                    {
                        tokenInfo.Token = (int)CMakeToken.Variable;
                        SetVariableDepth(ref state, originalVariableDepth);
                    }
                    else if (isFileName)
                    {
                        tokenInfo.Token = (int)CMakeToken.FileName;
                    }
                    else
                    {
                        tokenInfo.Token = (int)CMakeToken.NumericIdentifier;
                    }
                    return(true);
                }
                else if (_source[_offset] == '$')
                {
                    // Scan a variable start token.
                    CMakeToken varToken = _varTokenMap.FirstOrDefault(
                        x => _source.Substring(_offset).StartsWith(x.Value)).Key;
                    tokenInfo.StartIndex = _offset;
                    _offset++;
                    if (varToken != CMakeToken.Unspecified)
                    {
                        if (varToken != CMakeToken.GeneratorStart)
                        {
                            SetVariableDepth(ref state, originalVariableDepth + 1);
                        }
                        tokenInfo.Token   = (int)varToken;
                        tokenInfo.Trigger =
                            TokenTriggers.MemberSelect | TokenTriggers.MatchBraces;
                        _offset += _varTokenMap[varToken].Length - 1;
                    }
                    tokenInfo.EndIndex = _offset - 1;
                    tokenInfo.Color    = TokenColor.Identifier;
                    return(true);
                }
                else if (_source[_offset] == '}')
                {
                    // Scan a variable end token.
                    tokenInfo.StartIndex = _offset;
                    tokenInfo.EndIndex   = _offset;
                    tokenInfo.Color      = TokenColor.Identifier;
                    tokenInfo.Token      = (int)CMakeToken.VariableEnd;
                    tokenInfo.Trigger    = TokenTriggers.MatchBraces;
                    SetVariableDepth(ref state, originalVariableDepth > 0 ?
                                     originalVariableDepth - 1 : 0);
                    _offset++;
                    return(true);
                }
                else if (_source[_offset] == '>')
                {
                    // Scan a generator expression end token.
                    tokenInfo.StartIndex = _offset;
                    tokenInfo.EndIndex   = _offset;
                    tokenInfo.Color      = TokenColor.Identifier;
                    tokenInfo.Token      = (int)CMakeToken.GeneratorEnd;
                    tokenInfo.Trigger    = TokenTriggers.MatchBraces;
                    SetVariableDepth(ref state, originalVariableDepth > 0 ?
                                     originalVariableDepth - 1 : 0);
                    _offset++;
                    return(true);
                }
                else if (_source[_offset] == ':')
                {
                    // Scan a colon.
                    tokenInfo.StartIndex = _offset;
                    tokenInfo.EndIndex   = _offset;
                    tokenInfo.Color      = TokenColor.Identifier;
                    tokenInfo.Token      = (int)CMakeToken.Colon;
                    _offset++;
                    return(true);
                }
                else if (_source[_offset] == '[')
                {
                    // Scan a bracket argument, if it is one.
                    int i = _offset + 1;
                    while (i < _source.Length && _source[i] == '=')
                    {
                        i++;
                    }
                    if (i < _source.Length && _source[i] == '[')
                    {
                        SetBracketArgumentFlag(ref state, true);
                        ScanBracketCommentOrArgument(tokenInfo, false, ref state);
                        _lastWhitespace = false;
                        return(true);
                    }
                }
                _offset++;
            }
            return(false);
        }