|
// 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);
}
}
|