示例#1
0
 public IWhitespaceManager GetOrCreateWhitespaceManager(
     ITextBuffer buffer,
     NewlineState initialNewlineState,
     LeadingWhitespaceState initialLeadingWhitespaceState)
 {
     return(buffer.Properties.GetOrCreateSingletonProperty(
                typeof(IWhitespaceManager),
                () => new WhitespaceManager(buffer, initialNewlineState, initialLeadingWhitespaceState)));
 }
 public WhitespaceManager(ITextBuffer documentBuffer, NewlineState newlineState, LeadingWhitespaceState leadingWhitespaceState)
 {
     documentBuffer.Changed += this.OnDocumentBufferChanged;
     NewlineState            = newlineState;
     LeadingWhitespaceState  = leadingWhitespaceState;
 }
        internal static StringRebuilder Load(TextReader reader, long fileSize,
                                             out NewlineState newlineState,
                                             out LeadingWhitespaceState leadingWhitespaceState,
                                             out int longestLineLength,
                                             int blockSize = 0,
                                             int minCompressedBlockSize    = TextImageLoader.BlockSize,                                          // Exposed for unit tests
                                             bool throwOnInvalidCharacters = false)
        {
            newlineState           = new NewlineState();
            leadingWhitespaceState = new LeadingWhitespaceState();

            int currentLineLength = 0;

            longestLineLength = 0;
            char thresholdForInvalidCharacters = throwOnInvalidCharacters ? '\u0001' : '\0';    // Basically the only invalid character is \0, if we are looking for invalid characters.

            bool useCompressedStringRebuilders = (fileSize >= TextModelOptions.CompressedStorageFileSizeThreshold);

            if (blockSize == 0)
            {
                blockSize = useCompressedStringRebuilders ? TextModelOptions.CompressedStoragePageSize : TextImageLoader.BlockSize;
            }

            PageManager pageManager = null;

            char[] buffer;
            if (useCompressedStringRebuilders)
            {
                pageManager = new PageManager();
                buffer      = new char[blockSize];
            }
            else
            {
                buffer = TextImageLoader.AcquireBuffer(blockSize);
            }

            StringRebuilder content = StringRebuilderForChars.Empty;

            try
            {
                bool nextCharIsStartOfLine = true;
                while (true)
                {
                    int read = TextImageLoader.LoadNextBlock(reader, buffer);

                    if (read == 0)
                    {
                        break;
                    }

                    var lineBreaks = TextImageLoader.ParseBlock(
                        buffer,
                        read,
                        thresholdForInvalidCharacters,
                        ref newlineState,
                        ref leadingWhitespaceState,
                        ref currentLineLength,
                        ref longestLineLength,
                        ref nextCharIsStartOfLine);

                    char[] bufferForStringBuilder = buffer;
                    if (read < (buffer.Length / 2))
                    {
                        // We read far less characters than buffer so copy the contents to a new buffer and reuse the original buffer.
                        bufferForStringBuilder = new char[read];
                        Array.Copy(buffer, bufferForStringBuilder, read);
                    }
                    else
                    {
                        // We're using most of buffer so allocate a new block for the next chunk.
                        buffer = new char[blockSize];
                    }

                    var newContent = (useCompressedStringRebuilders && (read > minCompressedBlockSize))
                                     ? StringRebuilderForCompressedChars.Create(new Page(pageManager, bufferForStringBuilder, read), lineBreaks)
                                     : StringRebuilderForChars.Create(bufferForStringBuilder, read, lineBreaks);

                    content = content.Insert(content.Length, newContent);
                }

                longestLineLength = Math.Max(longestLineLength, currentLineLength);
            }
            finally
            {
                if (!useCompressedStringRebuilders)
                {
                    TextImageLoader.ReleaseBuffer(buffer);
                }
            }

            return(content);
        }
        // Evil performance hack (but we are on a hot path here):
        //  thresholdForInvalidCharacters should be '\u0001' if we are throwing on invalid characters.
        //                                should be '\0' if we are not.
        // (otherwise we need to check both a throwOnInvalidCharacters boolean and that c == 0).
        private static ILineBreaks ParseBlock(char[] buffer, int length, char thresholdForInvalidCharacters,
                                              ref NewlineState newlineState,
                                              ref LeadingWhitespaceState leadingWhitespaceState,
                                              ref int currentLineLength,
                                              ref int longestLineLength,
                                              ref bool nextCharIsStartOfLine)
        {
            // Note that the lineBreaks created here will (internally) use the pooled list of line breaks.
            IPooledLineBreaksEditor lineBreaks = LineBreakManager.CreatePooledLineBreakEditor(length);

            int index = 0;

            while (index < length)
            {
                int breakLength = TextUtilities.LengthOfLineBreak(buffer, index, length);
                if (breakLength == 0)
                {
                    char c = buffer[index];

                    // If we are checking for invalid characters, throw if we encounter a \0
                    if (c < thresholdForInvalidCharacters)
                    {
                        throw new FileFormatException("File contains NUL characters");
                    }

                    ++currentLineLength;
                    ++index;

                    if (nextCharIsStartOfLine)
                    {
                        switch (c)
                        {
                        case ' ':
                            leadingWhitespaceState.Increment(LeadingWhitespaceState.LineLeadingCharacter.Space, 1);
                            break;

                        case '\t':
                            leadingWhitespaceState.Increment(LeadingWhitespaceState.LineLeadingCharacter.Tab, 1);
                            break;

                        default:
                            leadingWhitespaceState.Increment(LeadingWhitespaceState.LineLeadingCharacter.Printable, 1);
                            break;
                        }

                        nextCharIsStartOfLine = false;
                    }
                }
                else
                {
                    lineBreaks.Add(index, breakLength);
                    longestLineLength = Math.Max(longestLineLength, currentLineLength);
                    currentLineLength = 0;


                    if (breakLength == 2)
                    {
                        newlineState.Increment(NewlineState.LineEnding.CRLF, 1);
                    }
                    else
                    {
                        switch (buffer[index])
                        {
                        // This code needs to be kep consistent with TextUtilities.LengthOfLineBreak()
                        case '\r': newlineState.Increment(NewlineState.LineEnding.CR, 1); break;

                        case '\n': newlineState.Increment(NewlineState.LineEnding.LF, 1); break;

                        case '\u0085': newlineState.Increment(NewlineState.LineEnding.NEL, 1); break;

                        case '\u2028': newlineState.Increment(NewlineState.LineEnding.LS, 1); break;

                        case '\u2029': newlineState.Increment(NewlineState.LineEnding.PS, 1); break;

                        default: throw new InvalidOperationException("Unexpected line ending");
                        }
                    }

                    if (nextCharIsStartOfLine)
                    {
                        leadingWhitespaceState.Increment(LeadingWhitespaceState.LineLeadingCharacter.Empty, 1);
                    }

                    nextCharIsStartOfLine = true;
                }

                index += breakLength;
            }

            lineBreaks.ReleasePooledLineBreaks();

            return(lineBreaks);
        }