Private/Split-DiffToken.ps1

#Requires -Version 5.1

function Split-DiffToken {
    <#
    .SYNOPSIS
        1 行を、行内比較の単位(トークン)へ分割する。
    .DESCRIPTION
        英数字とアンダースコアの連続を 1 つのトークンとし、それ以外の文字は
        1 文字ずつ独立したトークンにします。トークンを連結すると元の文字列に一致します。
    .PARAMETER Text
        分割する 1 行。空文字も受け取ります。
    .OUTPUTS
        [string[]] トークンの配列。Text が空文字なら空配列。
    .EXAMPLE
        Split-DiffToken -Text 'varying(100),'
        # 'varying' / '(' / '100' / ')' / ','
    .EXAMPLE
        Split-DiffToken -Text '2026-08-17'
        # '2026' / '-' / '08' / '-' / '17'
    #>

    [CmdletBinding()]
    [OutputType([string[]])]
    param(
        [Parameter(Mandatory)] [AllowEmptyString()] [string]$Text
    )

    $tokens = [System.Collections.Generic.List[string]]::new()
    if ($Text.Length -eq 0) { return $tokens.ToArray() }

    $buffer = [System.Text.StringBuilder]::new()
    foreach ($ch in $Text.ToCharArray()) {
        # [A-Za-z0-9_] だけを語の構成文字とする。Char.IsLetterOrDigit では日本語も語に含まれ、
        # 長い日本語の文字列が 1 トークンになって、変更箇所を示せなくなる。
        # 記号を 1 文字ずつのトークンにするのは、括弧やハイフンをまたぐ変更を、その部分だけ示すため。
        $isWord = ($ch -ge 'a' -and $ch -le 'z') -or ($ch -ge 'A' -and $ch -le 'Z') -or ($ch -ge '0' -and $ch -le '9') -or ($ch -ceq '_')
        if ($isWord) {
            [void]$buffer.Append($ch)
        }
        else {
            if ($buffer.Length -gt 0) { $tokens.Add($buffer.ToString()); [void]$buffer.Clear() }
            $tokens.Add([string]$ch)
        }
    }
    if ($buffer.Length -gt 0) { $tokens.Add($buffer.ToString()) }

    return $tokens.ToArray()
}