-
-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathSet-RegEx.ps1
158 lines (147 loc) · 6.28 KB
/
Set-RegEx.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
function Set-RegEx
{
<#
.Synopsis
Sets a Regular Expression
.Description
Sets Regular Expressions to a .regex.txt file
.Link
Use-RegEx
.Example
Write-RegEx -Name Digits -CharacterClass Digit -Repeat |
Set-RegEx
#>
[CmdletBinding(SupportsShouldProcess=$true)]
[OutputType([Nullable])]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingCmdletAliases", "", Justification="Irregular Uses Smart Aliases")]
param(
# The regular expression.
[Parameter(Mandatory,Position=0,ValueFromPipelineByPropertyName)]
[string]
$Pattern,
# The name of the regular expression. If not provided, this can be inferred if the pattern starts with a capture
[Parameter(Position=1,ValueFromPipelineByPropertyName)]
[string]
$Name,
# The description
[Parameter(ValueFromPipelineByPropertyName)]
[string]
$Description,
# The path to the file. If this is not provided, it will save regular expressions to the user's Irregular module path.
[Parameter(ValueFromPipelineByPropertyName)]
[string]
$Path,
# If set, will not save the regular expression to disk. Instead, it will alter the in-memory RegEx library.
# To use the alias immediately, call Set-RegEx with the . operator (e.g. . Set-Regex -Pattern '\s+' -Name Whitespace -Temporary)
[Parameter(ValueFromPipelineByPropertyName)]
[switch]
$Temporary,
# The timeout for the regular expression.
# This will only be used if the expression is temporary.
# By default, this is 5 seconds
[TimeSpan]
$TimeOut = '00:00:05',
# If set, will output created files or commands.
# If using -Temporary, will output the created commands.
# Otherwise, will output the created files.
[switch]
$PassThru
)
process {
#region Find the Preferred Save Location
if (-not $path -and -not $Temporary) { # If haven't been provided a path
$modulePaths = # find the preferred path
$env:PSModulePath -split $(if ($PSVersionTable.Platform -eq 'Unix') { ':' } else {';' })
$preferredPath =
foreach ($mp in $modulePaths) { # It's the first valid module path
if ($PSVersionTable.Platform -eq 'Unix' -and $mp -like '/usr/*') { # beneath /usr/ (on Unix)
$mp;break
}
if ($PSVersionTable.Platform -ne 'Unix' -and $mp -like "$env:USERPROFILE*") { # or %userprofile% (on Windows)
$mp;break
}
}
if (-not $preferredPath) { return }
$path = $preferredPath.TrimEnd([IO.Path]::DirectorySeparatorChar), 'Irregular', 'RegEx' -join [IO.Path]::DirectorySeparatorChar
if (-not [IO.Directory]::Exists($Path)) {
$path = New-Item -ItemType Directory -Path $path -Force
if (-not $path) { return }
}
}
#endregion Find the Preferred Save Location
$patternLines = $Pattern -split '(?>\r\n|\n)'
$inlineDescription = [Collections.ArrayList]::new()
$patternLines = @(foreach ($pl in $patternLines) {
if ($pl.Trim().StartsWith('#')) {
$inlineDescription.AddRange(@($pl))
} elseif ($pl) {
$pl
}
})
$StartWithCapture = ?<StartsWithCapture> # (?<StartsWithCapture>\A\(\?\<(?<FirstCaptureName>\w+))>
$rawPattern = $patternLines -join [Environment]::NewLine
if ($rawPattern -match $StartWithCapture -and -not $Name) {
$Name = $Matches.FirstCaptureName
}
if (-not $Name) {
Write-Error "Must provide a -Name, or start the pattern with a named capture." -ErrorId Irregular.Missing.Name -Category NotSpecified
return
}
if ($rawPattern -match $StartWithCapture -and $name -eq $Matches.FirstCaptureName) {
$removeTheName = ($rawPattern -replace $StartWithCapture, '').Trim().Trim([Environment]::NewLine).Trim()
if ($removeTheName.EndsWith(')')) {
$removeTheName = $removeTheName.Substring(0, $removeTheName.Length - 1)
}
$rawPattern = $removeTheName
}
$descriptionLines =
@(foreach ($d in $Description -split '(?>\r\n|\n)') {
if ($d -and $d.Trim() -notlike '#*') {
"# $d"
} else {
$d
}
}) + $inlineDescription
$patternFileContent =
if ($descriptionLines) {
$($descriptionLines -join [Environment]::Newline) +
[Environment]::Newline +
$rawPattern
} else {
$rawPattern
}
if ($Temporary) {
# If we didn't have a regex library, create one.
if (-not $script:_RegexLibrary) { $script:_RegexLibrary = @{} }
$script:_RegexLibrary[$Name] = [Regex]::new("(?<$($Name)>
$rawPattern
)","IgnoreCase,IgnorePatternWhitespace", '00:00:05.00')
$tempModule =
New-Module -Name "?<$Name>" -ScriptBlock {
Set-Alias "?<$args>" Use-RegEx; Export-ModuleMember -Alias *
} -ArgumentList $name |
Import-Module -Global -PassThru
if (-not $script:_RegexTempModules) {
$script:_RegexTempModules = [Collections.Queue]::new()
}
$script:_RegexTempModules.Enqueue($tempModule)
if ($passThru) {
$tempModule.ExportedCommands.Values
}
} else {
$semiResolvedPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path)
$path =
if ([IO.Directory]::Exists($semiResolvedPath)) {
"$semiResolvedPath".TrimEnd([IO.Path]::DirectorySeparatorChar), "$Name.regex.txt" -join [IO.Path]::DirectorySeparatorChar
} elseif ($semiResolvedPath -like '*.regex.*') {
$semiResolvedPath
}
if ($Path) {
$patternFileContent | Set-Content -Path $path -Force -Encoding UTF8
if ($PassThru) {
Get-Item -Path $Path
}
}
}
}
}