core/powershell/utils.ps1
Add-Member -InputObject $PSCompletions -MemberType ScriptMethod ConvertFrom_JsonToHashtable { param([string]$json) # Handle json string $matches = [regex]::Matches($json, '\s*"\s*"\s*:') foreach ($match in $matches) { $json = $json -replace $match.Value, "`"empty_key_$([System.Guid]::NewGuid().Guid)`":" } $json = [regex]::Replace($json, ",`n?(\s*`n)?\}", "}") function ConvertToHashtable { param($obj) $hash = @{} if ($obj -is [System.Management.Automation.PSCustomObject]) { foreach ($_ in $obj | Get-Member -MemberType Properties) { $k = $_.Name # Key $v = $obj.$k # Value if ($v -is [System.Collections.IEnumerable] -and $v -isnot [string]) { # Handle array $arr = @() foreach ($item in $v) { $arr += if ($item -is [System.Management.Automation.PSCustomObject]) { ConvertToHashtable($item) }else { $item } } $hash[$k] = $arr } elseif ($v -is [System.Management.Automation.PSCustomObject]) { # Handle object $hash[$k] = ConvertToHashtable($v) } else { $hash[$k] = $v } } } else { $hash = $obj } $hash } # Recurse ConvertToHashtable ($json | ConvertFrom-Json) } Add-Member -InputObject $PSCompletions -MemberType ScriptMethod start_job { $PSCompletions.job = Start-Job -ScriptBlock { param($PSCompletions) $null = Start-Job -ScriptBlock { param($PSCompletions) function convert_from_json_to_hashtable { param( [Parameter(ValueFromPipeline = $true)] [string]$json ) $matches = [regex]::Matches($json, '\s*"\s*"\s*:') foreach ($match in $matches) { $json = $json -replace $match.Value, "`"empty_key_$([System.Guid]::NewGuid().Guid)`":" } $json = [regex]::Replace($json, ",`n?(\s*`n)?\}", "}") function ConvertToHashtable { param($obj) $hash = @{} if ($obj -is [System.Management.Automation.PSCustomObject]) { foreach ($_ in $obj | Get-Member -MemberType Properties) { $k = $_.Name # Key $v = $obj.$k # Value if ($v -is [System.Collections.IEnumerable] -and $v -isnot [string]) { # Handle array $arr = @() foreach ($item in $v) { $arr += if ($item -is [System.Management.Automation.PSCustomObject]) { ConvertToHashtable($item) }else { $item } } $hash[$k] = $arr } elseif ($v -is [System.Management.Automation.PSCustomObject]) { # Handle object $hash[$k] = ConvertToHashtable($v) } else { $hash[$k] = $v } } } else { $hash = $obj } $hash } # Recurse ConvertToHashtable ($json | ConvertFrom-Json) } function get_raw_content { param ([string]$path, [bool]$trim = $true) $res = Get-Content $path -Raw -Encoding utf8 -ErrorAction SilentlyContinue if ($res) { if ($trim) { return $res.Trim() } return $res } return '' } function set_config { param ([string]$k, [string]$v) $c = get_raw_content $PScompletions.path.config | convert_from_json_to_hashtable $c.$k = $v $c | ConvertTo-Json -Depth 100 -Compress | Out-File $PScompletions.path.config -Encoding utf8 -Force } function download_list { if (!(Test-Path $PScompletions.path.completions_json)) { @{ list = @('psc') } | ConvertTo-Json -Compress | Out-File $PScompletions.path.completions_json -Encoding utf8 -Force } $current_list = (get_raw_content $PScompletions.path.completions_json | ConvertFrom-Json).list if ($PScompletions.url) { try { $content = (Invoke-WebRequest -Uri "$($PScompletions.url)/completions.json").Content | ConvertFrom-Json $remote_list = $content.list $diff = Compare-Object $remote_list $current_list -PassThru if ($diff) { $diff | Out-File $PScompletions.path.change -Force -Encoding utf8 $content | ConvertTo-Json -Depth 100 -Compress | Out-File $PScompletions.path.completions_json -Encoding utf8 -Force } else { Clear-Content $PScompletions.path.change -Force } } catch {} } } download_list # ensure completion config foreach ($_ in $PSCompletions.cmd.Keys) { $path = "$($PSCompletions.path.completions)/$($_)/config.json" $json = get_raw_content $path | ConvertFrom-Json $path = "$($PSCompletions.path.completions)/$($_)/language/$($json.language[0]).json" $json = get_raw_content $path | convert_from_json_to_hashtable if ($json.config) { foreach ($item in $json.config) { if ($PSCompletions.config.comp_config.$_.$($item.name) -in @('', $null)) { $PSCompletions.config.comp_config.$_.$($item.name) = $item.value $need_update_config = $true } } } } if ($need_update_config) { $PSCompletions.config | ConvertTo-Json -Depth 100 -Compress | Out-File $PSCompletions.path.config -Encoding utf8 -Force } # check version try { if ($PSCompletions.config.module_update -eq 1) { $response = Invoke-WebRequest -Uri "$($PSCompletions.url)/module/version.txt" $content = $response.Content.Trim() $versions = @($PSCompletions.version, $content) | Sort-Object { [Version] $_ } if ($versions[-1] -ne $PSCompletions.version) { set_config 'module_update' $versions[-1] } } } catch {} # check update if ($PSCompletions.config.update -eq 1) { $update_list = [System.Collections.Generic.List[string]]@() foreach ($_ in Get-ChildItem $PSCompletions.path.completions -ErrorAction SilentlyContinue | Where-Object { $_.Name -in $PSCompletions.list }) { try { $response = Invoke-WebRequest -Uri "$($PSCompletions.url)/completions/$($_.Name)/guid.txt" $content = $response.Content.Trim() $guid = get_raw_content "$($PSCompletions.path.completions)/$($_.Name)/guid.txt" if ($guid -ne $content) { $update_list.Add($_.Name) } } catch {} } if ($update_list) { $update_list | Out-File $PSCompletions.path.update -Force -Encoding utf8 } else { Clear-Content $PSCompletions.path.update -Force } } } -ArgumentList $PSCompletions $PSCompletions.wc = New-Object System.Net.WebClient function convert_from_json_to_hashtable { param( [Parameter(ValueFromPipeline = $true)] [string]$json ) $matches = [regex]::Matches($json, '\s*"\s*"\s*:') foreach ($match in $matches) { $json = $json -replace $match.Value, "`"empty_key_$([System.Guid]::NewGuid().Guid)`":" } $json = [regex]::Replace($json, ",`n?(\s*`n)?\}", "}") function ConvertToHashtable { param($obj) $hash = @{} if ($obj -is [System.Management.Automation.PSCustomObject]) { foreach ($_ in $obj | Get-Member -MemberType Properties) { $k = $_.Name # Key $v = $obj.$k # Value if ($v -is [System.Collections.IEnumerable] -and $v -isnot [string]) { # Handle array $arr = @() foreach ($item in $v) { $arr += if ($item -is [System.Management.Automation.PSCustomObject]) { ConvertToHashtable($item) }else { $item } } $hash[$k] = $arr } elseif ($v -is [System.Management.Automation.PSCustomObject]) { # Handle object $hash[$k] = ConvertToHashtable($v) } else { $hash[$k] = $v } } } else { $hash = $obj } $hash } # Recurse ConvertToHashtable ($json | ConvertFrom-Json) } function get_raw_content { param ([string]$path, [bool]$trim = $true) $res = Get-Content $path -Raw -Encoding utf8 -ErrorAction SilentlyContinue if ($res) { if ($trim) { return $res.Trim() } return $res } return '' } function get_language { param ([string]$completion) $path_config = "$($PSCompletions.path.completions)/$($completion)/config.json" if (!(Test-Path $path_config) -or !( get_raw_content $path_config)) { try { $PSCompletions.wc.DownloadFile("$($PSCompletions.url)/completions/$($completion)/config.json", $path_config) } catch {} } $content_config = (get_raw_content $path_config) | ConvertFrom-Json if ($PSCompletions.config.comp_config.$completion -and $PSCompletions.config.comp_config.$completion.language) { $config_language = $PSCompletions.config.comp_config.$completion.language } else { $config_language = $null } if ($config_language) { $language = if ($config_language -in $content_config.language) { $config_language }else { $content_config.language[0] } } else { $language = if ($PSCompletions.language -in $content_config.language) { $PSCompletions.language }else { $content_config.language[0] } } return $language } $completion_datas = @{} Get-ChildItem $PSCompletions.path.completions -Include "order.json" -File -Recurse | Where-Object { $_.LastWriteTime -gt (Get-Date).AddMonths(-1) } | ForEach-Object { $cmd = Split-Path (Split-Path $_.FullName -Parent) -Leaf if ($cmd -in $PSCompletions.cmd.Keys) { $language = get_language $cmd $path_language = "$($PSCompletions.path.completions)/$($cmd)/language/$($language).json" if (Test-Path $path_language) { $completion_datas.$cmd = (get_raw_content $path_language) | convert_from_json_to_hashtable } else { try { $PSCompletions.wc.DownloadFile("$($PSCompletions.url)/completions/$($cmd)/language/$($language).json", $path_language) $completion_datas.$cmd = (get_raw_content $path_language) | convert_from_json_to_hashtable } catch {} } } } return $completion_datas } -ArgumentList $PSCompletions } Add-Member -InputObject $PSCompletions -MemberType ScriptMethod order_job { param($completions, $history_path, $root, $path_order) $PSCompletions.order."$($root)_job" = Start-Job -ScriptBlock { param($PScompletions, $completions, $path_history, $root, $path_order) $order = [ordered]@{} $index = 1 foreach ($_ in $completions) { $order.$($_.name -join ' ') = $index $index++ } $historys = [System.Collections.Generic.List[string]]@() foreach ($_ in Get-Content $path_history -Encoding utf8 -ErrorAction SilentlyContinue) { foreach ($alias in $PSCompletions.cmd.$root) { if ($_ -match "^[^\S\n]*$($alias)\s+.+") { $historys.Add($_) break } } } $index = -1 function handle_order { param([array]$history) $str = $history -join ' ' if ($str -in $order.Keys) { $order.$str = $index } if ($history.Count -eq 1) { return } else { handle_order $history[0..($history.Count - 2)] } } foreach ($_ in $historys) { $matches = [regex]::Matches($_, "(?:`"[^`"]*`"|'[^']*'|\S)+") $cmd = [System.Collections.Generic.List[string]]@() foreach ($m in $matches) { $cmd.Add($m.Value) } if ($cmd.Count -gt 1) { handle_order $cmd[1..($cmd.Count - 1)] $index-- } } $json = $order | ConvertTo-Json -Depth 100 -Compress $matches = [regex]::Matches($json, '\s*"\s*"\s*:') foreach ($match in $matches) { $json = $json -replace $match.Value, "`"empty_key_$([System.Guid]::NewGuid().Guid)`":" } $json | Out-File $path_order -Encoding utf8 -Force return $order } -ArgumentList $PScompletions, $completions, $history_path, $root, $path_order } |