Ejemplo n.º 1
0
        internal MatchResult TryMatchOrRegionStart(Scanner s, out int idxMatchedItem)
        {
            if (null == this.m_communalRegex && !this.m_manualOnly)
            {
                this.BuildCommunalRegex();
            }

            if (this.m_manualOnly)
            {
                return(this.TryManualMatch(s, out idxMatchedItem));
            }

            Match m = this.m_communalRegex.Match(s.InputWindow.Window, s.Reader.PosCurrent - s.PosWindowStart);

            if (!m.Success)
            {
                // communal regex didn't match, manual matches are the only hope
                return(this.TryManualMatch(s, out idxMatchedItem));
            }

            int         idxCommunalMatchedItem = int.MaxValue;
            MatchResult communalResult;

            // we got a match in the communal regex. Now we need to find out which syntax item matched
            for (int i = 1; i < this.m_cntGroupsInCommunalRegex; i++)
            {
                if (m.Groups[i].Success && this.m_IdxItemFromGroupNumber[i] != -1)
                {
                    idxCommunalMatchedItem = this.m_IdxItemFromGroupNumber[i];
                    break;
                }
            }

            ContainerItem matchedItem = (ContainerItem)this.m_items[idxCommunalMatchedItem];

            matchedItem.BuildResult(s, out communalResult, m);

            // now, let's try the manual matches as well so we can then pick the best match
            MatchResult manualResult;
            int         idxManualMatchedItem;

            manualResult = this.TryManualMatch(s, out idxManualMatchedItem);

            // Ok. At this point, we know that the communal regex matched. However, that does not necessarily mean that
            // the syntax item can start successfully. This happens because a region sometimes needs to satisfy other
            // conditions for it to start (eg, oneline regions). So, we now check if the communal match really was succesful.
            if (!communalResult.Success)
            {
                // ok, so the communal item fell through. Now we need to walk all of the items that come _later_ than the
                // communal item that fell through. This is because we know that no one _before_ it could have matched,
                // or else the communal regex would have picked that.

                for (idxCommunalMatchedItem++; idxManualMatchedItem < this.m_items.Length; idxManualMatchedItem++)
                {
                    if (idxManualMatchedItem < idxCommunalMatchedItem)
                    {
                        break;
                    }

                    communalResult = ((ContainerItem)this.m_items[idxManualMatchedItem]).TryMatch(s);
                    if (communalResult.Success)
                    {
                        break;
                    }
                }

                if (!communalResult.Success)
                {
                    idxCommunalMatchedItem = int.MaxValue;
                }
            }

            // at this point, we could have a successful communal item and/or a successful manual item, or both could have failed
            // if both failed, the caller will get a result with success set to false either way. If both worked, we return the
            // highest priority match
            if (idxManualMatchedItem < idxCommunalMatchedItem)
            {
                idxMatchedItem = idxManualMatchedItem;
                return(manualResult);
            }
            else
            {
                idxMatchedItem = idxCommunalMatchedItem;
                return(communalResult);
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Builds a regex that captures the start patterns for all vim matches and region starts in this <see cref="SetOfSyntaxItems" />.
        /// Some patterns are not included and must be run manually (eg, the ones with <see cref="Pattern.LeadingContext">leading context</see>).
        /// </summary>
        private void BuildCommunalRegex()
        {
            if (!this.HasMatchesOrRegions)
            {
                string msg = StringExtensions.Fi("Invalid call to build regex for matches and regions on '{0}', which does "
                                                 + "not have any matches or regions.", this);
                throw new InvalidOperationException(msg);
            }

            this.EnsureRunTime();

            List <int>    idxsManualMatches    = new List <int>(2);
            bool          canDoExplicitCapture = true;
            StringBuilder sb = new StringBuilder(30 * (this.Items.Length - this.IdxFirstNonKeyword));

            sb.Append(@"\G(");

            bool addedItem = false;

            for (int idxItem = this.IdxFirstNonKeyword; idxItem < this.m_items.Length; idxItem++)
            {
                ContainerItem item = (ContainerItem)this.Items[idxItem];

                string regex;
                bool   isManualMatch;
                item.GetMatchRegex(out isManualMatch, out regex);

                if (isManualMatch)
                {
                    idxsManualMatches.Add(idxItem);
                    continue;
                }

                if (addedItem)
                {
                    sb.Append("|");
                }

                sb.AppendFormat("(?<i{0}>{1})", idxItem, regex);
                canDoExplicitCapture = canDoExplicitCapture && item.CanDoExplicitCapture;
                addedItem            = true;
            }

            sb.Append(")");

            if (0 < idxsManualMatches.Count)
            {
                this.m_idxsManualMatchItems = idxsManualMatches.ToArray();
            }

            if (!addedItem)
            {
                this.m_manualOnly = true;
                return;
            }

            RegexOptions options = RegexOptions.Compiled;

            if (canDoExplicitCapture)
            {
                options |= RegexOptions.ExplicitCapture;
            }

            this.m_communalRegex = new Regex(sb.ToString(), options);

            this.m_cntGroupsInCommunalRegex  = this.m_communalRegex.GetGroupNumbers().Length;
            this.m_IdxItemFromGroupNumber    = new int[this.m_cntGroupsInCommunalRegex];
            this.m_IdxItemFromGroupNumber[0] = -1;

            string[] groupNames = this.m_communalRegex.GetGroupNames();
            for (int i = 1; i < groupNames.Length; i++)
            {
                string groupName = groupNames[i];

                int idxMatchedItem;
                if (groupName[0] == 'i')
                {
                    idxMatchedItem = int.Parse(groupName.Substring(1), CultureInfo.InvariantCulture);
                }
                else
                {
                    idxMatchedItem = -1;
                }

                this.m_IdxItemFromGroupNumber[i] = idxMatchedItem;
            }
        }
Ejemplo n.º 3
0
        private bool TryVimMatchesAndRegions(ref int posAdvanceTo)
        {
            int posMatchAt = this.Reader.PosCurrent;

            if (this.m_pendingMatch.PosMatchStart < posMatchAt)
            {
                this.m_pendingMatch.PosMatchStart = int.MaxValue;
            }

            if (this.m_posDontMatchUntil < posMatchAt && this.TopScope.ActiveSyntaxItems.HasMatchesOrRegions)
            {
                int idxMatchedItem;

                MatchResult result = this.TopScope.ActiveSyntaxItems.TryMatchOrRegionStart(this, out idxMatchedItem);

                for (int i = idxMatchedItem; i < this.TopScope.ActiveSyntaxItems.Items.Length; i++)
                {
                    bool firstIteration = idxMatchedItem == i;

                    if (!firstIteration)
                    {
                        result = ((ContainerItem)this.TopScope.ActiveSyntaxItems.Items[i]).TryMatch(this);
                    }

                    ContainerItem matchCandidate = result.ContainerItem;

                    // the check below prevents infinite recursion when an item contains itself. It's still possible
                    // to get infinite recursion using two items that contain each other.
                    if ((matchCandidate == this.ItemInScope) && (posMatchAt == this.TopScope.PosMatchedAt))
                    {
                        continue;
                    }


                    if (result.Success)
                    {
                        if (result.ContainerItem is VimMatch && result.PosAfterMatch == posMatchAt)
                        {
                            continue;
                        }

                        if (result.PosMatchStart < this.m_pendingMatch.PosMatchStart ||
                            (result.PosMatchStart == this.m_pendingMatch.PosMatchStart && this.m_pendingMatch.PositionInSyntaxFile < matchCandidate.PositionInSyntaxDefinition)
                            )
                        {
                            this.m_pendingMatch = result;
                            this.m_pendingMatch.PositionInSyntaxFile = matchCandidate.PositionInSyntaxDefinition;

                            // if the match starts at the match head, we'll call it the best possible match. In _theory_, we could find a match with
                            // a negative start offset, but this would always force us to try every match, which would kill performance. Vim behaves like
                            // we do. I imagine it's for the same reason.
                            if (result.PosMatchStart <= posMatchAt)
                            {
                                break;
                            }
                        }
                    }
                }
            }

            if (this.m_pendingMatch.PosMatchStart <= posMatchAt)
            {
                ContainerItem matchedItem = this.m_pendingMatch.ContainerItem;
                matchedItem.StartScopeForSuccesfulMatch(this, this.m_pendingMatch);
                this.ClearPendingMatch();

                posAdvanceTo = this.TopScope.PosStart;
                return(true);
            }

            return(false);
        }