|
// 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.
#nullable disable
using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.Extensions;
internal static class DocumentExtensions
{
public static IList<Tuple<TextSpan, uint>> GetVisibleCodeBlocks(this Document document, CancellationToken cancellationToken)
{
var codeBlocks = new List<Tuple<TextSpan, uint>>();
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var text = document.GetTextSynchronously(cancellationToken);
var start = 0;
uint cookie = 0;
foreach (var line in text.Lines)
{
var trivia = document.GetSyntaxRootSynchronously(cancellationToken).FindTrivia(line.Start);
// We should only see structured trivia here
if (trivia.HasStructure)
{
var directive = trivia.GetStructure();
if (syntaxFacts.IsDirective(directive))
{
if (syntaxFacts.TryGetExternalSourceInfo(directive, out var info))
{
// Is this start of a line directive? if so, then add this new entry
if (info.StartLine.HasValue)
{
// The token's .Value is a boxed integer, so we need to unbox and then cast to get a uint
cookie = (uint)info.StartLine.Value;
start = text.Lines[line.LineNumber + 1].Start;
}
else if (info.Ends)
{
// This is the end of this code block, so end TextSpanAndCookie we previously added
var previousLine = text.Lines[line.LineNumber - 1];
// If the #region and #endregion are on consecutive lines, we don't want to end up with
// end before start.
var end = Math.Max(start, previousLine.End);
codeBlocks.Add(Tuple.Create(TextSpan.FromBounds(start, end), cookie));
}
}
}
}
}
return codeBlocks;
}
}
|