1
+ # PowerShell Editor Services Bootstrapper Script
2
+ # ----------------------------------------------
3
+ # This script contains startup logic for the PowerShell Editor Services
4
+ # module when launched by an editor. It handles the following tasks:
5
+ #
6
+ # - Verifying the existence of dependencies like PowerShellGet
7
+ # - Verifying that the expected version of the PowerShellEditorServices module is installed
8
+ # - Installing the PowerShellEditorServices module if confirmed by the user
9
+ # - Finding unused TCP port numbers for the language and debug services to use
10
+ # - Starting the language and debug services from the PowerShellEditorServices module
11
+ #
12
+ # NOTE: If editor integration authors make modifications to this
13
+ # script, please consider contributing changes back to the
14
+ # canonical version of this script at the PowerShell Editor
15
+ # Services GitHub repository:
16
+ #
17
+ # https://github.com/PowerShell/PowerShellEditorServices/blob/master/module/Start-EditorServices.ps1
18
+
1
19
param (
2
20
[Parameter (Mandatory = $true )]
3
21
[ValidateNotNullOrEmpty ()]
@@ -19,16 +37,6 @@ param(
19
37
[string ]
20
38
$HostVersion ,
21
39
22
- [Parameter (Mandatory = $true )]
23
- [ValidateNotNullOrEmpty ()]
24
- [string ]
25
- $LanguageServicePipeName ,
26
-
27
- [Parameter (Mandatory = $true )]
28
- [ValidateNotNullOrEmpty ()]
29
- [string ]
30
- $DebugServicePipeName ,
31
-
32
40
[ValidateNotNullOrEmpty ()]
33
41
[string ]
34
42
$BundledModulesPath ,
@@ -40,28 +48,141 @@ param(
40
48
$LogLevel ,
41
49
42
50
[switch ]
43
- $WaitForCompletion ,
51
+ $WaitForDebugger ,
44
52
45
53
[switch ]
46
- $WaitForDebugger
54
+ $ConfirmInstall
47
55
)
48
56
57
+ # This variable will be assigned later to contain information about
58
+ # what happened while attempting to launch the PowerShell Editor
59
+ # Services host
60
+ $resultDetails = $null ;
61
+
62
+ function Test-ModuleAvailable ($ModuleName , $ModuleVersion ) {
63
+ $modules = Get-Module - ListAvailable $moduleName
64
+ if ($modules -ne $null ) {
65
+ if ($ModuleVersion -ne $null ) {
66
+ foreach ($module in $modules ) {
67
+ if ($module.Version.Equals ($moduleVersion )) {
68
+ return $true ;
69
+ }
70
+ }
71
+ }
72
+ else {
73
+ return $true ;
74
+ }
75
+ }
76
+
77
+ return $false ;
78
+ }
79
+
80
+ function Test-PortAvailability ($PortNumber ) {
81
+ $portAvailable = $true ;
82
+
83
+ try {
84
+ $ipAddress = [System.Net.Dns ]::GetHostEntryAsync(" localhost" ).Result.AddressList[0 ];
85
+ $tcpListener = [System.Net.Sockets.TcpListener ]::new($ipAddress , $portNumber );
86
+ $tcpListener.Start ();
87
+ $tcpListener.Stop ();
88
+
89
+ }
90
+ catch [System.Net.Sockets.SocketException ] {
91
+ # Check the SocketErrorCode to see if it's the expected exception
92
+ if ($error [0 ].Exception.InnerException.SocketErrorCode -eq [System.Net.Sockets.SocketError ]::AddressAlreadyInUse) {
93
+ $portAvailable = $false ;
94
+ }
95
+ else {
96
+ Write-Output (" Error code: " + $error [0 ].SocketErrorCode)
97
+ }
98
+ }
99
+
100
+ return $portAvailable ;
101
+ }
102
+
103
+ $rand = [System.Random ]::new()
104
+ function Get-AvailablePort {
105
+ $triesRemaining = 10 ;
106
+
107
+ while ($triesRemaining -gt 0 ) {
108
+ $port = $rand.Next (10000 , 30000 )
109
+ if ((Test-PortAvailability - PortAvailability $port ) -eq $true ) {
110
+ return $port
111
+ }
112
+
113
+ $triesRemaining -- ;
114
+ }
115
+
116
+ return $null
117
+ }
118
+
49
119
# Add BundledModulesPath to $env:PSModulePath
50
120
if ($BundledModulesPath ) {
51
- $env: PSModulePath = $BundledModulesPath + " ; " + $env: PSModulePath
121
+ $env: PSMODULEPATH = $BundledModulesPath + [ System.IO.Path ]::PathSeparator + $env: PSMODULEPATH
52
122
}
53
123
124
+ # Check if PowerShellGet module is available
125
+ if ((Test-ModuleAvailable " PowerShellGet" ) -eq $false ) {
126
+ # TODO: WRITE ERROR
127
+ }
128
+
129
+ # Check if the expected version of the PowerShell Editor Services
130
+ # module is installed
54
131
$parsedVersion = [System.Version ]::new($EditorServicesVersion )
132
+ if ((Test-ModuleAvailable " PowerShellEditorServices" - RequiredVersion $parsedVersion ) -eq $false ) {
133
+ if ($ConfirmInstall ) {
134
+ # TODO: Check for error and return failure if necessary
135
+ Install-Module " PowerShellEditorServices" - RequiredVersion $parsedVersion - Confirm
136
+ }
137
+ else {
138
+ # Indicate to the client that the PowerShellEditorServices module
139
+ # needs to be installed
140
+ Write-Output " needs_install"
141
+ }
142
+ }
143
+
55
144
Import-Module PowerShellEditorServices - RequiredVersion $parsedVersion - ErrorAction Stop
56
145
57
- Start-EditorServicesHost `
58
- - HostName $HostName `
59
- - HostProfileId $HostProfileId `
60
- - HostVersion $HostVersion `
61
- - LogPath $LogPath `
62
- - LogLevel $LogLevel `
63
- - LanguageServicePipeName $LanguageServicePipeName `
64
- - DebugServicePipeName $DebugServicePipeName `
65
- - BundledModulesPath $BundledModulesPath `
66
- - WaitForCompletion:$WaitForCompletion.IsPresent `
67
- - WaitForDebugger:$WaitForDebugger.IsPresent
146
+ # Locate available port numbers for services
147
+ $languageServicePort = Get-AvailablePort
148
+ $debugServicePort = Get-AvailablePort
149
+
150
+ $editorServicesHost =
151
+ Start-EditorServicesHost `
152
+ - HostName $HostName `
153
+ - HostProfileId $HostProfileId `
154
+ - HostVersion $HostVersion `
155
+ - LogPath $LogPath `
156
+ - LogLevel $LogLevel `
157
+ - LanguageServicePort $languageServicePort `
158
+ - DebugServicePort $debugServicePort `
159
+ - BundledModulesPath $BundledModulesPath `
160
+ - WaitForDebugger:$WaitForDebugger.IsPresent
161
+
162
+ # TODO: Verify that the service is started
163
+
164
+ $resultDetails = @ {
165
+ " status" = " started" ;
166
+ " channel" = " tcp" ;
167
+ " languageServicePort" = $languageServicePort ;
168
+ " debugServicePort" = $debugServicePort ;
169
+ };
170
+
171
+ # Notify the client that the services have started
172
+ Write-Output (ConvertTo-Json - InputObject $resultDetails - Compress)
173
+
174
+ try {
175
+ # Wait for the host to complete execution before exiting
176
+ $editorServicesHost.WaitForCompletion ()
177
+ }
178
+ catch [System.Exception ] {
179
+ $e = $_.Exception ; # .InnerException;
180
+ $errorString = " "
181
+
182
+ while ($e -ne $null ) {
183
+ $errorString = $errorString + ($e.Message + " `r`n " + $e.StackTrace + " `r`n " )
184
+ $e = $e.InnerException ;
185
+ }
186
+
187
+ Write-Error (" `r`n Caught error while waiting for EditorServicesHost to complete:`r`n " + $errorString )
188
+ }
0 commit comments