Skip to content

Commit f1b0e75

Browse files
Refactor handling of CommandSuite settings file (#23)
- Generate default settings file using StringBuilder instead of PSStringTemplate - Add function Import-CommandSuite to start initialization on explicit module import. - Add a FileSystemWatcher to watch for changes to the settings file - Add on module remove event handler to dispose of the FileSystemWatcher and unregister editor commands - Fix an issue where an internal setting would be added to the default settings file
1 parent a04d113 commit f1b0e75

11 files changed

+182
-242
lines changed

README.md

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,32 +33,21 @@ Install-Module EditorServicesCommandSuite -Scope CurrentUser
3333

3434
```powershell
3535
# Place this in your VSCode profile
36-
Import-Module EditorServicesCommandSuite
37-
Import-EditorCommand -Module EditorServicesCommandSuite
36+
Import-CommandSuite
3837
```
3938

4039
```powershell
4140
# Or copy this command and paste it into the integrated console
42-
psedit $profile;$psEditor|% g*t|% c*e|% i* "Import-Module EditorServicesCommandSuite`nImport-EditorCommand -Module EditorServicesCommandSuite`n" 1 1 1 1
41+
psedit $profile;$psEditor|% g*t|% c*e|% i* "Import-CommandSuite`n" 1 1 1 1
4342
```
4443

4544
## Importing
4645

47-
You can call any of the editor commands as functions like you would from any module. Or you can import them as registered editor commands in Editor Services.
48-
49-
### Import all Editor Commands
50-
5146
```powershell
52-
Import-Module EditorServicesCommandSuite
53-
Import-EditorCommand -Module EditorServicesCommandSuite
47+
Import-CommandSuite
5448
```
5549

56-
### Import specific Editor Commands
57-
58-
```powershell
59-
Import-Module EditorServicesCommandSuite
60-
Get-Command ConvertTo-MarkdownHelp, ConvertTo-SplatExpression | Import-EditorCommand
61-
```
50+
This function will import all editor commands in the module and initialize event handlers.
6251

6352
## Using Editor Commands
6453

docs/en-US/EditorServicesCommandSuite.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ The ConvertTo-MarkdownHelp function will replace existing CBH (comment based hel
4646
The ConvertTo-SplatExpression function transforms a CommandAst to use a splat expression instead
4747
of inline parameters.
4848

49+
### [Import-CommandSuite](Import-CommandSuite.md)
50+
51+
The Import-CommandSuite function imports the EditorServicesCommandSuite module and initalizes internal processes like setting up event handlers. You can import the module directly without using this function, but it isn't supported and may cause unexpected behavior. This function can be invoked after the module is loaded in case of accidental or auto loading.
52+
4953
### [Expand-Expression](Expand-Expression.md)
5054

5155
The Expand-Expression function replaces text at a specified range with it's output in PowerShell. As an editor command it will expand output of selected text.

docs/en-US/Import-CommandSuite.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
---
2+
external help file: EditorServicesCommandSuite-help.xml
3+
online version: https://github.com/SeeminglyScience/EditorServicesCommandSuite/blob/master/docs/en-US/Import-CommandSuite.md
4+
schema: 2.0.0
5+
---
6+
7+
# Import-CommandSuite
8+
9+
## SYNOPSIS
10+
11+
Initialize the EditorServicesCommandSuite module.
12+
13+
## SYNTAX
14+
15+
```powershell
16+
Import-CommandSuite
17+
```
18+
19+
## DESCRIPTION
20+
21+
The Import-CommandSuite function imports the EditorServicesCommandSuite module and initalizes internal processes like setting up event handlers. You can import the module directly without using this function, but it isn't supported and may cause unexpected behavior. This function can be invoked after the module is loaded in case of accidental or auto loading.
22+
23+
## EXAMPLES
24+
25+
### -------------------------- EXAMPLE 1 --------------------------
26+
27+
```powershell
28+
Import-CommandSuite
29+
```
30+
31+
Imports EditorServicesCommandSuite functions, editor commands, and event handlers.
32+
33+
## PARAMETERS
34+
35+
## INPUTS
36+
37+
### None
38+
39+
This function does not accept input from the pipeline.
40+
41+
## OUTPUTS
42+
43+
### None
44+
45+
This function does not output to the pipeline.
46+
47+
## NOTES
48+
49+
## RELATED LINKS

module/Classes/MemberExpressionGeneration.ps1

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ using namespace System.Reflection
33
using namespace System.Text
44

55
class MemberExpressionGeneration {
6-
static [string] $Indent = ' ';
6+
# Work around for type resolution issues.
7+
hidden static [string] $Indent = ('TextOps' -as [type])::Indent
78

89
[MemberInfo] $Member;
910
[psobject] $Target;

module/Classes/TextOps.ps1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ enum CaseType {
55
}
66

77
class TextOps {
8+
static [string] $Indent = ' ';
9+
810
static [string] TransformCase([psobject] $text, [CaseType] $type) {
911
$methodName = 'ToUpperInvariant'
1012
if ($type -eq [CaseType]::Camel) {

module/EditorServicesCommandSuite.psd1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ FunctionsToExport = 'Add-CommandToManifest',
5555
'Expand-Expression',
5656
'Expand-MemberExpression',
5757
'Expand-TypeImplementation',
58+
'Import-CommandSuite',
5859
'New-ESCSSettingsFile',
5960
'Remove-Semicolon',
6061
'Set-HangingIndent',
@@ -83,6 +84,7 @@ FileList = 'EditorServicesCommandSuite.psd1',
8384
'Public\Expand-Expression.ps1',
8485
'Public\Expand-MemberExpression.ps1',
8586
'Public\Expand-TypeImplementation.ps1',
87+
'Public\Import-CommandSuite.ps1',
8688
'Public\New-ESCSSettingsFile.ps1',
8789
'Public\Remove-Semicolon.ps1',
8890
'Public\Set-HangingIndent.ps1',

module/Private/GetSettings.ps1

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,46 @@
11
function GetSettings {
2-
[CmdletBinding()]
3-
param()
2+
[CmdletBinding(DefaultParameterSetName='Auto')]
3+
param(
4+
[Parameter(ParameterSetName='Auto')]
5+
[switch] $Auto,
6+
7+
[Parameter(ParameterSetName='Reload')]
8+
[switch] $ForceReload,
9+
10+
[Parameter(ParameterSetName='Default')]
11+
[switch] $ForceDefault
12+
)
413
end {
14+
function GetSettingsFile {
15+
$importLocalizedDataSplat = @{
16+
BaseDirectory = $psEditor.Workspace.Path
17+
FileName = 'ESCSSettings.psd1'
18+
}
19+
20+
return Import-LocalizedData @importLocalizedDataSplat
21+
}
22+
523
function GetHashtable {
624
if ($script:CSSettings) { return $script:CSSettings }
725

826
if (-not [string]::IsNullOrWhiteSpace($psEditor.Workspace.Path)) {
927
$targetPath = Join-Path $psEditor.Workspace.Path -ChildPath 'ESCSSettings.psd1'
1028

1129
if (Test-Path $targetPath) {
12-
$importLocalizedDataSplat = @{
13-
BaseDirectory = $psEditor.Workspace.Path
14-
FileName = 'ESCSSettings.psd1'
15-
}
16-
17-
$script:CSSettings = Import-LocalizedData @importLocalizedDataSplat
18-
return $script:CSSettings
30+
return GetSettingsFile
1931
}
2032
}
2133

22-
$script:CSSettings = $script:DEFAULT_SETTINGS
34+
return $script:DEFAULT_SETTINGS
35+
}
2336

24-
return $script:CSSettings
37+
$settings = switch ($PSCmdlet.ParameterSetName) {
38+
Auto { GetHashtable }
39+
Reload { GetSettingsFile }
40+
Default { $script:DEFAULT_SETTINGS }
2541
}
2642

27-
$settings = GetHashtable
43+
$script:CSSettings = $settings
2844

2945
# Ensure all settings have a default value even if not present in user supplied file.
3046
if ($settings.PreValidated) { return $settings }
@@ -34,6 +50,7 @@ function GetSettings {
3450
$settings.Add($setting.Key, $setting.Value)
3551
}
3652
}
53+
3754
$settings.PreValidated = $true
3855
return $settings
3956
}

module/Public/Import-CommandSuite.ps1

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
function Import-CommandSuite {
2+
<#
3+
.EXTERNALHELP EditorServicesCommandSuite-help.xml
4+
#>
5+
[CmdletBinding()]
6+
param()
7+
end {
8+
Import-EditorCommand -Module EditorServicesCommandSuite
9+
if ([string]::IsNullOrWhiteSpace($psEditor.Workspace.Path)) {
10+
return
11+
}
12+
13+
$watcher = [System.IO.FileSystemWatcher]::new(
14+
<# path: #> $psEditor.Workspace.Path,
15+
<# filter: #> 'ESCSSettings.psd1')
16+
17+
$watcher.NotifyFilter = 'LastWrite, CreationTime, FileName'
18+
$watcher.EnableRaisingEvents = $true
19+
$subscribers = & {
20+
$guid = [guid]::NewGuid().ToString('n')
21+
$identifier = 'EditorServicesCommandSuite-{0}-{1}'
22+
$eventSplat = @{
23+
InputObject = $watcher
24+
SupportEvent = $true
25+
}
26+
27+
# Register event subscribers to handle changes to the settings file.
28+
$eventSplat.SourceIdentifier = ($identifier -f $guid, 'Changed')
29+
$null = Register-ObjectEvent @eventSplat -EventName Changed -Action {
30+
$module = Get-Module EditorServicesCommandSuite
31+
$null = & $module { GetSettings -ForceReload }
32+
}
33+
34+
$eventSplat.SourceIdentifier = ($identifier -f $guid, 'Created')
35+
$null = Register-ObjectEvent @eventSplat -EventName Created -Action {
36+
$module = Get-Module EditorServicesCommandSuite
37+
$null = & $module { GetSettings -ForceReload }
38+
}
39+
40+
$eventSplat.SourceIdentifier = ($identifier -f $guid, 'Deleted')
41+
$null = Register-ObjectEvent @eventSplat -EventName Deleted -Action {
42+
$module = Get-Module EditorServicesCommandSuite
43+
$null = & $module { GetSettings -ForceDefault }
44+
}
45+
46+
# yield
47+
Get-EventSubscriber -SourceIdentifier "EditorServicesCommandSuite-$guid*" -Force
48+
}
49+
50+
$script:SETTINGS_WATCHER = $watcher
51+
$script:SETTINGS_SUBSCRIBERS = $subscribers
52+
53+
# When the module is removed, remove the editor commands it registered and dispose of
54+
# event subscribers.
55+
$ExecutionContext.SessionState.Module.OnRemove = {
56+
$editorCommands = $ExecutionContext.SessionState.Module.ExportedFunctions.Values | Where-Object {
57+
$PSItem.ScriptBlock.Ast.Body.ParamBlock.Attributes.TypeName.Name -contains
58+
'EditorCommand'
59+
}
60+
61+
foreach ($command in $editorCommands) {
62+
$psEditor.UnregisterCommand($command.Name -replace '-')
63+
}
64+
65+
$script:SETTINGS_SUBSCRIBERS | Unregister-Event -Force
66+
$script:SETTINGS_WATCHER.Dispose()
67+
}
68+
}
69+
}

module/Public/New-ESCSSettingsFile.ps1

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ function New-ESCSSettingsFile {
55
.EXTERNALHELP EditorServicesCommandSuite-help.xml
66
#>
77
[CmdletBinding()]
8-
[SuppressMessage('PSAvoidShouldContinueWithoutForce', '',
9-
Justification='ShouldContinue is called from a subroutine without CmdletBinding.')]
8+
[SuppressMessage(
9+
'PSAvoidShouldContinueWithoutForce', '',
10+
Justification='ShouldContinue is called from a subroutine without CmdletBinding.')]
1011
param(
1112
[ValidateNotNullOrEmpty()]
1213
[string]
@@ -41,27 +42,29 @@ function New-ESCSSettingsFile {
4142
$targetFilePath = Join-Path $Path -ChildPath 'ESCSSettings.psd1'
4243
HandleFileExists $targetFilePath
4344

44-
try {
45-
$groupDefinition = Get-Content $PSScriptRoot\..\Templates\SettingsFile.stg -Raw -ErrorAction Stop
45+
$builder = [System.Text.StringBuilder]::new().Append('@{')
4646

47-
$templateSplat = @{
48-
Group = (New-StringTemplateGroup -Definition $groupDefinition)
49-
Name = 'Base'
50-
Parameters = @{
51-
Settings = $script:DEFAULT_SETTINGS.GetEnumerator()
52-
Strings = [pscustomobject]$Strings
53-
}
47+
$null = foreach ($setting in $DEFAULT_SETTINGS.GetEnumerator()) {
48+
if ($settings.Key -eq 'PreValidated') {
49+
continue
5450
}
5551

56-
$content = Invoke-StringTemplate @templateSplat
57-
} catch {
58-
ThrowError -Exception ([InvalidOperationException]::new($Strings.TemplateGroupCompileError)) `
59-
-Id TemplateGroupCompileError `
60-
-Category InvalidOperation `
61-
-Target $groupDefinition
62-
return
52+
$builder.AppendLine().Append([TextOps]::Indent)
53+
$resourceStringName = 'SettingComment{0}' -f $setting.Key
54+
if ($Strings.ContainsKey($resourceStringName)) {
55+
$builder.
56+
AppendFormat('# {0}', $Strings[$resourceStringName]).
57+
AppendLine().
58+
Append([TextOps]::Indent)
59+
}
60+
61+
$builder.
62+
AppendFormat('{0} = ''{1}''', $setting.Key, $setting.Value).
63+
AppendLine()
6364
}
6465

66+
$content = $builder.AppendLine('}').ToString()
67+
6568
$null = New-Item $targetFilePath -Value $content
6669
if ($psEditor) {
6770
try {

0 commit comments

Comments
 (0)