File: Workspaces\EditorTextFactoryService.cs
Web Access
Project: src\src\EditorFeatures\Core\Microsoft.CodeAnalysis.EditorFeatures.csproj (Microsoft.CodeAnalysis.EditorFeatures)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Utilities;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Editor.Implementation.Workspaces;
 
[ExportWorkspaceService(typeof(ITextFactoryService), ServiceLayer.Editor), Shared]
[method: ImportingConstructor]
[method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")]
internal sealed class EditorTextFactoryService(
    ITextBufferCloneService textBufferCloneService,
    ITextBufferFactoryService textBufferFactoryService,
    IContentTypeRegistryService contentTypeRegistryService) : ITextFactoryService
{
    private readonly ITextBufferCloneService _textBufferCloneService = textBufferCloneService;
    private readonly ITextBufferFactoryService _textBufferFactory = textBufferFactoryService;
    private readonly IContentType _unknownContentType = contentTypeRegistryService.UnknownContentType;
    private static readonly Encoding s_throwingUtf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
 
    public SourceText CreateText(Stream stream, Encoding? defaultEncoding, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken)
    {
        // this API is for a case where user wants us to figure out encoding from the given stream.
        // if defaultEncoding is given, we will use it if we couldn't figure out encoding used in the stream ourselves.
        RoslynDebug.Assert(stream != null);
        RoslynDebug.Assert(stream.CanSeek);
        RoslynDebug.Assert(stream.CanRead);
 
        if (defaultEncoding == null)
        {
            // Try UTF-8
            try
            {
                return CreateTextInternal(stream, s_throwingUtf8Encoding, checksumAlgorithm, cancellationToken);
            }
            catch (DecoderFallbackException)
            {
                // Try Encoding.Default
                defaultEncoding = Encoding.Default;
            }
        }
 
        try
        {
            return CreateTextInternal(stream, defaultEncoding, checksumAlgorithm, cancellationToken);
        }
        catch (DecoderFallbackException)
        {
            // TODO: the callers do not expect null (https://github.com/dotnet/roslyn/issues/43040)
            return null!;
        }
    }
 
    public SourceText CreateText(TextReader reader, Encoding? encoding, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken)
    {
        // this API is for a case where user just wants to create a source text with explicit encoding.
        var buffer = CreateTextBuffer(reader);
 
        // use the given encoding as it is.
        return buffer.CurrentSnapshot.AsRoslynText(_textBufferCloneService, encoding, checksumAlgorithm);
    }
 
    private ITextBuffer CreateTextBuffer(TextReader reader)
        => _textBufferFactory.CreateTextBuffer(reader, _unknownContentType);
 
    private SourceText CreateTextInternal(Stream stream, Encoding encoding, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken)
    {
        cancellationToken.ThrowIfCancellationRequested();
        stream.Seek(0, SeekOrigin.Begin);
 
        using var reader = new StreamReader(stream, encoding, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true);
 
        var buffer = CreateTextBuffer(reader);
        return buffer.CurrentSnapshot.AsRoslynText(_textBufferCloneService, reader.CurrentEncoding ?? Encoding.UTF8, checksumAlgorithm);
    }
}