|
// 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.Collections.Immutable;
using System.Diagnostics;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Shared.Extensions;
internal static class StringExtensions
{
public static int? GetFirstNonWhitespaceOffset(this string line)
{
Contract.ThrowIfNull(line);
for (var i = 0; i < line.Length; i++)
{
if (!char.IsWhiteSpace(line[i]))
{
return i;
}
}
return null;
}
public static int? GetLastNonWhitespaceOffset(this string line)
{
Contract.ThrowIfNull(line);
for (var i = line.Length - 1; i >= 0; i--)
{
if (!char.IsWhiteSpace(line[i]))
{
return i;
}
}
return null;
}
public static string GetLeadingWhitespace(this string lineText)
{
Contract.ThrowIfNull(lineText);
var firstOffset = lineText.GetFirstNonWhitespaceOffset();
return firstOffset.HasValue
? lineText[..firstOffset.Value]
: lineText;
}
public static string GetTrailingWhitespace(this string lineText)
{
Contract.ThrowIfNull(lineText);
var lastOffset = lineText.GetLastNonWhitespaceOffset();
return lastOffset.HasValue && lastOffset.Value < lineText.Length
? lineText[(lastOffset.Value + 1)..]
: string.Empty;
}
public static int GetTextColumn(this string text, int tabSize, int initialColumn)
{
var lineText = text.GetLastLineText();
if (text != lineText)
{
return lineText.GetColumnFromLineOffset(lineText.Length, tabSize);
}
return text.ConvertTabToSpace(tabSize, initialColumn, text.Length) + initialColumn;
}
public static int ConvertTabToSpace(this string textSnippet, int tabSize, int initialColumn, int endPosition)
{
Debug.Assert(tabSize > 0);
Debug.Assert(endPosition >= 0 && endPosition <= textSnippet.Length);
var column = initialColumn;
// now this will calculate indentation regardless of actual content on the buffer except TAB
for (var i = 0; i < endPosition; i++)
{
if (textSnippet[i] == '\t')
{
column += tabSize - column % tabSize;
}
else
{
column++;
}
}
return column - initialColumn;
}
public static int IndexOf(this string? text, Func<char, bool> predicate)
{
if (text == null)
{
return -1;
}
for (var i = 0; i < text.Length; i++)
{
if (predicate(text[i]))
{
return i;
}
}
return -1;
}
public static string GetFirstLineText(this string text)
{
var lineBreak = text.IndexOf('\n');
if (lineBreak < 0)
{
return text;
}
return text[..(lineBreak + 1)];
}
public static string GetLastLineText(this string text)
{
var lineBreak = text.LastIndexOf('\n');
if (lineBreak < 0)
{
return text;
}
return text[(lineBreak + 1)..];
}
public static bool ContainsLineBreak(this string text)
{
foreach (var ch in text)
{
if (ch is '\n' or '\r')
{
return true;
}
}
return false;
}
public static int GetNumberOfLineBreaks(this string text)
{
var lineBreaks = 0;
for (var i = 0; i < text.Length; i++)
{
if (text[i] == '\n')
{
lineBreaks++;
}
else if (text[i] == '\r')
{
if (i + 1 == text.Length || text[i + 1] != '\n')
{
lineBreaks++;
}
}
}
return lineBreaks;
}
public static bool ContainsTab(this string text)
{
// PERF: Tried replacing this with "text.IndexOf('\t')>=0", but that was actually slightly slower
foreach (var ch in text)
{
if (ch == '\t')
{
return true;
}
}
return false;
}
public static ImmutableArray<SymbolDisplayPart> ToSymbolDisplayParts(this string text)
=> [new SymbolDisplayPart(SymbolDisplayPartKind.Text, null, text)];
public static int GetColumnOfFirstNonWhitespaceCharacterOrEndOfLine(this string line, int tabSize)
{
var firstNonWhitespaceChar = line.GetFirstNonWhitespaceOffset();
if (firstNonWhitespaceChar.HasValue)
{
return line.GetColumnFromLineOffset(firstNonWhitespaceChar.Value, tabSize);
}
else
{
// It's all whitespace, so go to the end
return line.GetColumnFromLineOffset(line.Length, tabSize);
}
}
public static int GetColumnFromLineOffset(this string line, int endPosition, int tabSize)
{
Contract.ThrowIfNull(line);
Contract.ThrowIfFalse(0 <= endPosition && endPosition <= line.Length);
Contract.ThrowIfFalse(tabSize > 0);
return ConvertTabToSpace(line, tabSize, 0, endPosition);
}
public static int GetLineOffsetFromColumn(this string line, int column, int tabSize)
{
Contract.ThrowIfNull(line);
Contract.ThrowIfFalse(column >= 0);
Contract.ThrowIfFalse(tabSize > 0);
var currentColumn = 0;
for (var i = 0; i < line.Length; i++)
{
if (currentColumn >= column)
{
return i;
}
if (line[i] == '\t')
{
currentColumn += tabSize - (currentColumn % tabSize);
}
else
{
currentColumn++;
}
}
// We're asking for a column past the end of the line, so just go to the end.
return line.Length;
}
public static void AppendToAliasNameSet(this string? alias, ImmutableHashSet<string>.Builder builder)
{
if (RoslynString.IsNullOrWhiteSpace(alias))
{
return;
}
builder.Add(alias);
var caseSensitive = builder.KeyComparer == StringComparer.Ordinal;
Debug.Assert(builder.KeyComparer == StringComparer.Ordinal || builder.KeyComparer == StringComparer.OrdinalIgnoreCase);
if (alias.TryGetWithoutAttributeSuffix(caseSensitive, out var aliasWithoutAttribute))
{
builder.Add(aliasWithoutAttribute);
return;
}
builder.Add(alias.GetWithSingleAttributeSuffix(caseSensitive));
}
}
|