1- import { format } from './commands/format'
1+ /**********************************************************************
2+ * Extension entry point *
3+ *********************************************************************/
4+
25import * as vscode from 'vscode'
3- import {
4- createOutputChannel ,
5- onDidChangeConfiguration ,
6- registerCommand ,
7- } from './utilities/common/vscodeapi'
8- import { registerLogger , traceInfo , traceVerbose } from './utilities/common/log'
9- import { onDidChangePythonInterpreter } from './utilities/common/python'
10- import { LSPClient } from './lsp/lsp'
11- import { AuthenticationProviderTobikoCloud } from './auth/auth'
6+
7+ import { format } from './commands/format'
128import { signOut } from './commands/signout'
139import { signIn } from './commands/signin'
1410import { signInSpecifyFlow } from './commands/signinSpecifyFlow'
1511import { renderModel , reRenderModelForSourceFile } from './commands/renderModel'
1612import { stop } from './commands/stop'
1713import { printEnvironment } from './commands/printEnvironment'
18- import { isErr } from '@bus/result'
14+
15+ import {
16+ createOutputChannel ,
17+ onDidChangeConfiguration ,
18+ registerCommand ,
19+ } from './utilities/common/vscodeapi'
20+ import {
21+ registerLogger ,
22+ traceInfo ,
23+ traceVerbose ,
24+ traceError ,
25+ } from './utilities/common/log'
26+ import { onDidChangePythonInterpreter } from './utilities/common/python'
27+ import { sleep } from './utilities/sleep'
1928import { handleError } from './utilities/errors'
29+
2030import { selector , completionProvider } from './completion/completion'
2131import { LineagePanel } from './webviews/lineagePanel'
2232import { RenderedModelProvider } from './providers/renderedModelProvider'
23- import { sleep } from './utilities/sleep'
33+
2434import {
2535 controller as testController ,
2636 setupTestController ,
2737} from './tests/tests'
2838
39+ import { isErr } from '@bus/result'
40+ import { AuthenticationProviderTobikoCloud } from './auth/auth'
41+ import { LSPClient } from './lsp/lsp'
42+
43+ /** Singleton LSP client for the extension. */
2944let lspClient : LSPClient | undefined
3045
31- // This method is called when your extension is activated
32- // Your extension is activated the very first time the command is executed
46+ /** Handle to the (single) test controller disposable so we can replace it on restart. */
47+ let testControllerDisposable : vscode . Disposable | undefined
48+
3349export async function activate ( context : vscode . ExtensionContext ) {
3450 const extensionOutputChannel = createOutputChannel ( 'sqlmesh' )
3551 context . subscriptions . push (
@@ -38,7 +54,6 @@ export async function activate(context: vscode.ExtensionContext) {
3854 )
3955 traceInfo ( 'Activating SQLMesh extension' )
4056
41- traceInfo ( 'Registering authentication provider' )
4257 const authProvider = new AuthenticationProviderTobikoCloud ( )
4358 context . subscriptions . push (
4459 vscode . authentication . registerAuthenticationProvider (
@@ -48,33 +63,70 @@ export async function activate(context: vscode.ExtensionContext) {
4863 { supportsMultipleAccounts : false } ,
4964 ) ,
5065 )
51- traceInfo ( 'Authentication provider registered' )
5266
67+ const restartLsp = async ( invokedByUser = false ) : Promise < void > => {
68+ if ( ! lspClient ) {
69+ lspClient = new LSPClient ( )
70+ }
71+
72+ traceVerbose ( 'Restarting SQLMesh LSP client' )
73+ const result = await lspClient . restart ( invokedByUser )
74+ if ( isErr ( result ) ) {
75+ await handleError (
76+ authProvider ,
77+ restartLsp ,
78+ result . error ,
79+ 'LSP restart failed' ,
80+ )
81+ return
82+ }
83+
84+ // push once to avoid duplicate disposables on multiple restarts
85+ if ( ! context . subscriptions . includes ( lspClient ) ) {
86+ context . subscriptions . push ( lspClient )
87+ }
88+
89+ /* Replace the test controller each time we restart the client */
90+ if ( testControllerDisposable ) {
91+ testControllerDisposable . dispose ( )
92+ }
93+ testControllerDisposable = setupTestController ( lspClient )
94+ context . subscriptions . push ( testControllerDisposable )
95+ }
96+
97+ // commands needing the restart helper
5398 context . subscriptions . push (
5499 vscode . commands . registerCommand (
55100 'sqlmesh.signin' ,
56- signIn ( authProvider , async ( ) => {
57- traceInfo ( 'Restarting LSP after sign-in' )
58- await restart ( )
59- } ) ,
101+ signIn ( authProvider , ( ) => restartLsp ( ) ) ,
60102 ) ,
61- )
62- context . subscriptions . push (
63103 vscode . commands . registerCommand (
64104 'sqlmesh.signinSpecifyFlow' ,
65- signInSpecifyFlow ( authProvider , async ( ) => {
66- traceInfo ( 'Restarting LSP after sign-in' )
67- await restart ( )
68- } ) ,
105+ signInSpecifyFlow ( authProvider , ( ) => restartLsp ( ) ) ,
69106 ) ,
70- )
71- context . subscriptions . push (
72107 vscode . commands . registerCommand ( 'sqlmesh.signout' , signOut ( authProvider ) ) ,
73108 )
74109
110+ // Instantiate the LSP client (once)
75111 lspClient = new LSPClient ( )
112+ const startResult = await lspClient . start ( )
113+ if ( isErr ( startResult ) ) {
114+ await handleError (
115+ authProvider ,
116+ restartLsp ,
117+ startResult . error ,
118+ 'Failed to start LSP' ,
119+ )
120+ return // abort activation – nothing else to do
121+ }
122+
123+ context . subscriptions . push ( lspClient )
76124
77- // Create and register the rendered model provider
125+ // Initialize the test controller
126+ testControllerDisposable = setupTestController ( lspClient )
127+ context . subscriptions . push ( testControllerDisposable , testController )
128+
129+ // Register the rendered model provider
78130 const renderedModelProvider = new RenderedModelProvider ( )
79131 context . subscriptions . push (
80132 vscode . workspace . registerTextDocumentContentProvider (
@@ -91,7 +143,6 @@ export async function activate(context: vscode.ExtensionContext) {
91143 ) ,
92144 )
93145
94- // Register the webview
95146 const lineagePanel = new LineagePanel ( context . extensionUri , lspClient )
96147 context . subscriptions . push (
97148 vscode . window . registerWebviewViewProvider (
@@ -100,11 +151,10 @@ export async function activate(context: vscode.ExtensionContext) {
100151 ) ,
101152 )
102153
103- // Add file save listener for auto-rerendering models
154+ // Re‑render model automatically when its source file is saved
104155 context . subscriptions . push (
105156 vscode . workspace . onDidSaveTextDocument ( async document => {
106157 if (
107- lspClient &&
108158 renderedModelProvider . hasRenderedModelForSource (
109159 document . uri . toString ( true ) ,
110160 )
@@ -119,73 +169,23 @@ export async function activate(context: vscode.ExtensionContext) {
119169 } ) ,
120170 )
121171
122- const restart = async ( invokedByUser = false ) => {
123- if ( lspClient ) {
124- traceVerbose ( 'Restarting LSP client' )
125- const restartResult = await lspClient . restart ( invokedByUser )
126- if ( isErr ( restartResult ) ) {
127- return handleError (
128- authProvider ,
129- restart ,
130- restartResult . error ,
131- 'LSP restart failed' ,
132- )
133- }
134- context . subscriptions . push ( lspClient )
135- context . subscriptions . push ( setupTestController ( lspClient ) )
136- } else {
137- lspClient = new LSPClient ( )
138- const result = await lspClient . start ( invokedByUser )
139- if ( isErr ( result ) ) {
140- return handleError (
141- authProvider ,
142- restart ,
143- result . error ,
144- 'Failed to start LSP' ,
145- )
146- } else {
147- context . subscriptions . push ( lspClient )
148- context . subscriptions . push ( setupTestController ( lspClient ) )
149- }
150- }
151- }
152-
172+ // miscellaneous commands
153173 context . subscriptions . push (
154174 vscode . commands . registerCommand (
155175 'sqlmesh.format' ,
156- format ( authProvider , lspClient , restart ) ,
176+ format ( authProvider , lspClient , restartLsp ) ,
157177 ) ,
178+ registerCommand ( 'sqlmesh.restart' , ( ) => restartLsp ( true ) ) ,
179+ registerCommand ( 'sqlmesh.stop' , stop ( lspClient ) ) ,
180+ registerCommand ( 'sqlmesh.printEnvironment' , printEnvironment ( ) ) ,
158181 )
159182
160183 context . subscriptions . push (
161- onDidChangePythonInterpreter ( async ( ) => {
162- await restart ( )
163- } ) ,
164- onDidChangeConfiguration ( async ( ) => {
165- await restart ( )
166- } ) ,
167- registerCommand ( `sqlmesh.restart` , async ( ) => {
168- await restart ( true )
169- } ) ,
170- registerCommand ( `sqlmesh.stop` , stop ( lspClient ) ) ,
171- registerCommand ( `sqlmesh.printEnvironment` , printEnvironment ( ) ) ,
184+ onDidChangePythonInterpreter ( ( ) => restartLsp ( ) ) ,
185+ onDidChangeConfiguration ( ( ) => restartLsp ( ) ) ,
172186 )
173187
174- const result = await lspClient . start ( )
175- if ( isErr ( result ) ) {
176- return handleError (
177- authProvider ,
178- restart ,
179- result . error ,
180- 'Failed to start LSP' ,
181- )
182- } else {
183- context . subscriptions . push ( lspClient )
184- context . subscriptions . push ( setupTestController ( lspClient ) )
185- context . subscriptions . push ( testController )
186- }
187-
188- if ( lspClient && ! lspClient . hasCompletionCapability ( ) ) {
188+ if ( ! lspClient . hasCompletionCapability ( ) ) {
189189 context . subscriptions . push (
190190 vscode . languages . registerCompletionItemProvider (
191191 selector ,
@@ -194,12 +194,19 @@ export async function activate(context: vscode.ExtensionContext) {
194194 )
195195 }
196196
197- traceInfo ( 'Extension activated' )
197+ traceInfo ( 'SQLMesh extension activated' )
198198}
199199
200200// This method is called when your extension is deactivated
201201export async function deactivate ( ) {
202- if ( lspClient ) {
203- await lspClient . dispose ( )
202+ try {
203+ if ( testControllerDisposable ) {
204+ testControllerDisposable . dispose ( )
205+ }
206+ if ( lspClient ) {
207+ await lspClient . dispose ( )
208+ }
209+ } catch ( e ) {
210+ traceError ( `Error during deactivate: ${ e } ` )
204211 }
205212}
0 commit comments