Skip to content

Commit f1cd18f

Browse files
committed
fix: stopped server would just restart
- stopped server would just restart if something needed it - this adds explicit flag which indicates that it was stopped by a user
1 parent 82cdde0 commit f1cd18f

File tree

4 files changed

+111
-10
lines changed

4 files changed

+111
-10
lines changed

vscode/extension/src/commands/stop.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export const stop = (lspClient: LSPClient | undefined) => {
1111
return
1212
}
1313

14-
await lspClient.stop()
14+
await lspClient.stop(true)
1515
await window.showInformationMessage('LSP server stopped')
1616
traceInfo('LSP server stopped successfully')
1717
}

vscode/extension/src/extension.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,10 @@ export async function activate(context: vscode.ExtensionContext) {
115115
}),
116116
)
117117

118-
const restart = async () => {
118+
const restart = async (invokedByUser = false) => {
119119
if (lspClient) {
120120
traceVerbose('Restarting LSP client')
121-
const restartResult = await lspClient.restart()
121+
const restartResult = await lspClient.restart(invokedByUser)
122122
if (isErr(restartResult)) {
123123
return handleError(
124124
authProvider,
@@ -130,7 +130,7 @@ export async function activate(context: vscode.ExtensionContext) {
130130
context.subscriptions.push(lspClient)
131131
} else {
132132
lspClient = new LSPClient()
133-
const result = await lspClient.start()
133+
const result = await lspClient.start(invokedByUser)
134134
if (isErr(result)) {
135135
return handleError(
136136
authProvider,
@@ -159,7 +159,7 @@ export async function activate(context: vscode.ExtensionContext) {
159159
await restart()
160160
}),
161161
registerCommand(`sqlmesh.restart`, async () => {
162-
await restart()
162+
await restart(true)
163163
}),
164164
registerCommand(`sqlmesh.stop`, stop(lspClient)),
165165
registerCommand(`sqlmesh.printEnvironment`, printEnvironment()),

vscode/extension/src/lsp/lsp.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ export class LSPClient implements Disposable {
3636
*/
3737
private supportedMethodsState: SupportedMethodsState = { type: 'not-fetched' }
3838

39+
/**
40+
* Explicitly stopped remembers whether the LSP client has been explicitly stopped
41+
* by the user. This is used to prevent the client from being restarted unless the user
42+
* explicitly calls the `restart` method.
43+
*/
44+
private explicitlyStopped = false
45+
3946
constructor() {
4047
this.client = undefined
4148
}
@@ -54,7 +61,15 @@ export class LSPClient implements Disposable {
5461
return completion !== undefined
5562
}
5663

57-
public async start(): Promise<Result<undefined, ErrorType>> {
64+
public async start(
65+
overrideStoppedByUser = false,
66+
): Promise<Result<undefined, ErrorType>> {
67+
if (this.explicitlyStopped && !overrideStoppedByUser) {
68+
traceInfo(
69+
'LSP client has been explicitly stopped by user, not starting again.',
70+
)
71+
return ok(undefined)
72+
}
5873
if (!outputChannel) {
5974
outputChannel = window.createOutputChannel('sqlmesh-lsp')
6075
}
@@ -124,18 +139,24 @@ export class LSPClient implements Disposable {
124139
return ok(undefined)
125140
}
126141

127-
public async restart(): Promise<Result<undefined, ErrorType>> {
142+
public async restart(
143+
overrideByUser = false,
144+
): Promise<Result<undefined, ErrorType>> {
128145
await this.stop()
129-
return await this.start()
146+
return await this.start(overrideByUser)
130147
}
131148

132-
public async stop() {
149+
public async stop(stoppedByUser = false): Promise<void> {
133150
if (this.client) {
134151
await this.client.stop()
135152
this.client = undefined
136153
// Reset supported methods state when the client stops
137154
this.supportedMethodsState = { type: 'not-fetched' }
138155
}
156+
if (stoppedByUser) {
157+
this.explicitlyStopped = true
158+
traceInfo('SQLMesh LSP client stopped by user.')
159+
}
139160
}
140161

141162
public async dispose() {

vscode/extension/tests/stop.spec.ts

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ test('Stop server works', async ({ page, sharedCodeServer }) => {
1818

1919
// Navigate to code-server instance
2020
await openServerPage(page, tempDir, sharedCodeServer)
21-
await page.waitForSelector('[role="application"]', { timeout: 10000 })
2221

2322
// Wait for the models folder to be visible in the file explorer
2423
await page.waitForSelector('text=models')
@@ -52,3 +51,84 @@ test('Stop server works', async ({ page, sharedCodeServer }) => {
5251
'text="Failed to render model: LSP client not ready."',
5352
)
5453
})
54+
55+
test('Stopped server only restarts when explicitly requested', async ({
56+
page,
57+
sharedCodeServer,
58+
}) => {
59+
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'vscode-test-sushi-'))
60+
await fs.copy(SUSHI_SOURCE_PATH, tempDir)
61+
62+
await createPythonInterpreterSettingsSpecifier(tempDir)
63+
64+
// Navigate to code-server instance
65+
await openServerPage(page, tempDir, sharedCodeServer)
66+
67+
// Wait for the models folder to be visible in the file explorer
68+
await page.waitForSelector('text=models')
69+
70+
// Click on the models folder, excluding external_models
71+
await page
72+
.getByRole('treeitem', { name: 'models', exact: true })
73+
.locator('a')
74+
.click()
75+
76+
// Open the customers.sql model
77+
await page
78+
.getByRole('treeitem', { name: 'marketing.sql', exact: true })
79+
.locator('a')
80+
.click()
81+
await page.waitForSelector('text=grain')
82+
await waitForLoadedSQLMesh(page)
83+
84+
// Click on sushi.raw_marketing
85+
await page.getByText('sushi.raw_marketing;').click()
86+
87+
// Open the preview hover
88+
await runCommand(page, 'Show Definition Preview Hover')
89+
90+
// Assert that the hover is visible with text "Table of marketing status."
91+
await page.waitForSelector('text=Table of marketing status.', {
92+
timeout: 5_000,
93+
state: 'visible',
94+
})
95+
96+
// Hit Esc to close the hover
97+
await page.keyboard.press('Escape')
98+
99+
// Assert that the hover is no longer visible
100+
await page.waitForSelector('text=Table of marketing status.', {
101+
timeout: 5_000,
102+
state: 'hidden',
103+
})
104+
105+
// Stop the server
106+
await runCommand(page, 'SQLMesh: Stop Server')
107+
108+
// Await LSP server stopped message
109+
await page.waitForSelector('text=LSP server stopped')
110+
111+
// Open the preview hover again
112+
await runCommand(page, 'Show Definition Preview Hover')
113+
114+
// Assert that the hover is not visible
115+
await page.waitForSelector('text=Table of marketing status.', {
116+
timeout: 5_000,
117+
state: 'hidden',
118+
})
119+
120+
// Restart the server explicitly
121+
await runCommand(page, 'SQLMesh: Restart Server')
122+
123+
// Await LSP server started message
124+
await waitForLoadedSQLMesh(page)
125+
126+
// Open the preview hover again
127+
await runCommand(page, 'Show Definition Preview Hover')
128+
129+
// Assert that the hover is visible with text "Table of marketing status."
130+
await page.waitForSelector('text=Table of marketing status.', {
131+
timeout: 5_000,
132+
state: 'visible',
133+
})
134+
})

0 commit comments

Comments
 (0)