File: Internal\Editor\FSharpSmartIndentProvider.cs
Web Access
Project: src\src\VisualStudio\ExternalAccess\FSharp\Microsoft.CodeAnalysis.ExternalAccess.FSharp.csproj (Microsoft.CodeAnalysis.ExternalAccess.FSharp)
// 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;
using System.ComponentModel.Composition;
using System.Threading;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Editor.Implementation.SmartIndent;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Options;
using Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Indentation;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Utilities;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal.Editor
{
    [Export(typeof(ISmartIndentProvider))]
    [ContentType(ContentTypeNames.FSharpContentType)]
    internal sealed class FSharpSmartIndentProvider : ISmartIndentProvider
    {
#pragma warning disable CS0618 // Type or member is obsolete
        private readonly IFSharpSynchronousIndentationService? _legacyService;
#pragma warning restore
        private readonly IFSharpIndentationService? _service;
 
        private readonly IGlobalOptionService _globalOptions;
 
        [ImportingConstructor]
        [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
        public FSharpSmartIndentProvider(
            [Import(AllowDefault = true)] IFSharpSynchronousIndentationService? legacyService,
            [Import(AllowDefault = true)] IFSharpIndentationService? service,
            IGlobalOptionService globalOptions)
        {
            Contract.ThrowIfTrue(service == null && legacyService == null);
 
            _legacyService = legacyService;
            _service = service;
            _globalOptions = globalOptions;
        }
 
        public ISmartIndent? CreateSmartIndent(ITextView textView)
            => _globalOptions.GetOption(SmartIndenterOptionsStorage.SmartIndenter) ? new SmartIndent(textView, this) : null;
 
        private sealed class SmartIndent : ISmartIndent
        {
            private readonly ITextView _textView;
            private readonly FSharpSmartIndentProvider _provider;
 
            public SmartIndent(ITextView textView, FSharpSmartIndentProvider provider)
            {
                _textView = textView;
                _provider = provider;
            }
 
            public void Dispose()
            {
            }
 
            public int? GetDesiredIndentation(ITextSnapshotLine line)
                => GetDesiredIndentation(line, CancellationToken.None);
 
            private int? GetDesiredIndentation(ITextSnapshotLine line, CancellationToken cancellationToken)
            {
                using (Logger.LogBlock(FunctionId.SmartIndentation_Start, cancellationToken))
                {
                    var document = line.Snapshot.GetOpenDocumentInCurrentContextWithChanges();
 
                    // all F# documents should have a file path
                    if (document?.FilePath == null)
                        return null;
 
                    FSharpIndentationResult? result;
                    if (_provider._service != null)
                    {
                        var text = document.GetTextSynchronously(cancellationToken);
 
                        var indentStyle = _provider._globalOptions.GetOption(IndentationOptionsStorage.SmartIndent, document.Project.Language);
 
                        var fsharpOptions = new FSharpIndentationOptions(
                            TabSize: _textView.Options.GetOptionValue(DefaultOptions.TabSizeOptionId),
                            IndentStyle: (FormattingOptions.IndentStyle)indentStyle);
 
#pragma warning disable 0618 // Compat with existing EA api
                        result = _provider._service.GetDesiredIndentation(document.Project.LanguageServices, text, document.Id, document.FilePath, line.LineNumber, fsharpOptions);
#pragma warning restore
                    }
                    else
                    {
                        Contract.ThrowIfNull(_provider._legacyService);
                        result = _provider._legacyService.GetDesiredIndentation(document, line.LineNumber, cancellationToken);
                    }
 
                    return result.HasValue ? new IndentationResult(result.Value.BasePosition, result.Value.Offset).GetIndentation(_textView, line) : null;
                }
            }
        }
    }
}