private ImmutableArray<ClassifiedSpan> MergeParts(
                ArrayBuilder<ClassifiedSpan> syntaxParts,
                ArrayBuilder<ClassifiedSpan> semanticParts)
            {
                // Take all the syntax parts.  However, if any have been overridden by a 
                // semantic part, then choose that one.

                var finalParts = ArrayBuilder<ClassifiedSpan>.GetInstance();
                var lastReplacementIndex = 0;
                for (int i = 0, n = syntaxParts.Count; i < n; i++)
                {
                    var syntaxPartAndSpan = syntaxParts[i];

                    // See if we can find a semantic part to replace this syntax part.
                    var replacementIndex = semanticParts.FindIndex(
                        lastReplacementIndex, t => t.TextSpan == syntaxPartAndSpan.TextSpan);

                    // Take the semantic part if it's just 'text'.  We want to keep it if
                    // the semantic classifier actually produced an interesting result 
                    // (as opposed to it just being a 'gap' classification).
                    var part = replacementIndex >= 0 && !IsClassifiedAsText(semanticParts[replacementIndex])
                        ? semanticParts[replacementIndex]
                        : syntaxPartAndSpan;
                    finalParts.Add(part);

                    if (replacementIndex >= 0)
                    {
                        // If we found a semantic replacement, update the lastIndex.
                        // That way we can start searching from that point instead 
                        // of checking all the elements each time.
                        lastReplacementIndex = replacementIndex + 1;
                    }
                }

                return finalParts.ToImmutableAndFree();
            }