Skip to content

Commit 86a8026

Browse files
committed
fix: resolve cmd+enter shortcut and autocompletion text replacement issues
- Fix cmd+enter keyboard shortcut not executing queries by using ref callback pattern - Fix autocompletion not replacing partial text by calculating proper word range - Add word range calculation to IntellisenseProvider for correct text replacement - Update all suggestion methods to use calculated range instead of cursor position - Wrap executeQuery and handleExecuteQuery in useCallback to prevent closure issues
1 parent 8927b88 commit 86a8026

File tree

3 files changed

+75
-74
lines changed

3 files changed

+75
-74
lines changed

src/renderer/components/QueryWorkspace/QueryWorkspace.tsx

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,12 @@ export function QueryWorkspace({ connectionId, onOpenTableTab }: QueryWorkspaceP
3333
const [selectedText, setSelectedText] = useState('')
3434
const [showAIPanel, setShowAIPanel] = useState(false)
3535
const editorRef = useRef<any>(null)
36+
const executeQueryRef = useRef<() => void>()
3637

3738
const activeTab = tabs.find((tab) => tab.id === activeTabId)
3839
const activeResult = activeTab ? results[activeTab.id] : null
3940

40-
const handleEditorDidMount = (editor: any, monaco: Monaco) => {
41+
const handleEditorDidMount = useCallback((editor: any, monaco: Monaco) => {
4142
editorRef.current = editor
4243

4344
// Add keyboard shortcuts
@@ -51,10 +52,12 @@ export function QueryWorkspace({ connectionId, onOpenTableTab }: QueryWorkspaceP
5152
contextMenuGroupId: 'navigation',
5253
contextMenuOrder: 1.5,
5354
run: () => {
54-
handleExecuteQuery()
55+
if (executeQueryRef.current) {
56+
executeQueryRef.current()
57+
}
5558
}
5659
})
57-
}
60+
}, [])
5861

5962
// Tab management functions
6063
const handleNewTab = useCallback(() => {
@@ -128,40 +131,43 @@ export function QueryWorkspace({ connectionId, onOpenTableTab }: QueryWorkspaceP
128131
}
129132
}, [openTableTab, onOpenTableTab])
130133

131-
const executeQuery = async (queryToExecute: string) => {
132-
if (!activeTab || activeTab.type !== 'query') return
133-
134-
if (!queryToExecute.trim()) return
134+
const executeQuery = useCallback(
135+
async (queryToExecute: string) => {
136+
if (!activeTab || activeTab.type !== 'query') return
135137

136-
try {
137-
setIsExecuting(true)
138-
const startTime = Date.now()
138+
if (!queryToExecute.trim()) return
139139

140-
const queryResult = await window.api.database.query(connectionId, queryToExecute.trim())
141-
const executionTime = Date.now() - startTime
140+
try {
141+
setIsExecuting(true)
142+
const startTime = Date.now()
142143

143-
const result: QueryExecutionResult = {
144-
...queryResult,
145-
executionTime,
146-
rowCount: queryResult.data?.length || 0
147-
}
144+
const queryResult = await window.api.database.query(connectionId, queryToExecute.trim())
145+
const executionTime = Date.now() - startTime
148146

149-
setResults({ ...results, [activeTab.id]: result })
150-
} catch (error) {
151-
setResults({
152-
...results,
153-
[activeTab.id]: {
154-
success: false,
155-
message: 'Query execution failed',
156-
error: error instanceof Error ? error.message : 'Unknown error'
147+
const result: QueryExecutionResult = {
148+
...queryResult,
149+
executionTime,
150+
rowCount: queryResult.data?.length || 0
157151
}
158-
})
159-
} finally {
160-
setIsExecuting(false)
161-
}
162-
}
163152

164-
const handleExecuteQuery = async () => {
153+
setResults({ ...results, [activeTab.id]: result })
154+
} catch (error) {
155+
setResults({
156+
...results,
157+
[activeTab.id]: {
158+
success: false,
159+
message: 'Query execution failed',
160+
error: error instanceof Error ? error.message : 'Unknown error'
161+
}
162+
})
163+
} finally {
164+
setIsExecuting(false)
165+
}
166+
},
167+
[activeTab, results, connectionId]
168+
)
169+
170+
const handleExecuteQuery = useCallback(async () => {
165171
if (!activeTab || activeTab.type !== 'query') return
166172

167173
let queryToExecute = ''
@@ -176,7 +182,12 @@ export function QueryWorkspace({ connectionId, onOpenTableTab }: QueryWorkspaceP
176182
}
177183

178184
await executeQuery(queryToExecute)
179-
}
185+
}, [activeTab, selectedText, executeQuery])
186+
187+
// Update the ref whenever handleExecuteQuery changes
188+
useEffect(() => {
189+
executeQueryRef.current = handleExecuteQuery
190+
}, [handleExecuteQuery])
180191

181192
const handleExecuteQueryFromAI = async (sqlQuery: string) => {
182193
// Update the editor content with the SQL query

src/renderer/lib/intellisense/IntellisenseProvider.ts

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ export abstract class IntellisenseProvider {
2828
abstract getColumnSuggestions(
2929
context: SQLContext,
3030
schema: DatabaseSchema,
31-
position: Position
31+
position: Position,
32+
range: any
3233
): Monaco.languages.CompletionItem[]
3334

3435
async provideCompletionItems(model: any, position: Position): Promise<CompletionList> {
@@ -37,6 +38,15 @@ export abstract class IntellisenseProvider {
3738
const schema = await this.schemaCache.getSchema()
3839
const context = this.contextParser.parseContext(model, position)
3940

41+
// Get the word range at the current position for proper text replacement
42+
const wordInfo = model.getWordUntilPosition(position)
43+
const range = {
44+
startLineNumber: position.lineNumber,
45+
startColumn: wordInfo.startColumn,
46+
endLineNumber: position.lineNumber,
47+
endColumn: wordInfo.endColumn
48+
}
49+
4050
console.log('Context:', {
4151
type: context.type,
4252
currentWord: context.currentWord,
@@ -48,22 +58,22 @@ export abstract class IntellisenseProvider {
4858

4959
switch (context.type) {
5060
case 'keyword':
51-
suggestions.push(...this.getKeywordSuggestions(context, schema, position))
61+
suggestions.push(...this.getKeywordSuggestions(context, schema, position, range))
5262
break
5363
case 'from':
54-
suggestions.push(...this.getTableSuggestions(context, schema, position))
64+
suggestions.push(...this.getTableSuggestions(context, schema, position, range))
5565
break
5666
case 'column':
5767
case 'select':
5868
case 'where':
59-
suggestions.push(...this.getColumnSuggestions(context, schema, position))
60-
suggestions.push(...this.getFunctionSuggestions(context, schema, position))
69+
suggestions.push(...this.getColumnSuggestions(context, schema, position, range))
70+
suggestions.push(...this.getFunctionSuggestions(context, schema, position, range))
6171
break
6272
case 'function':
63-
suggestions.push(...this.getFunctionSuggestions(context, schema, position))
73+
suggestions.push(...this.getFunctionSuggestions(context, schema, position, range))
6474
break
6575
default:
66-
suggestions.push(...this.getGeneralSuggestions(context, schema, position))
76+
suggestions.push(...this.getGeneralSuggestions(context, schema, position, range))
6777
}
6878

6979
console.log('Total suggestions before filtering:', suggestions.length)
@@ -76,7 +86,8 @@ export abstract class IntellisenseProvider {
7686
protected getKeywordSuggestions(
7787
_context: SQLContext,
7888
_schema: DatabaseSchema,
79-
position: Position
89+
position: Position,
90+
range: any
8091
): Monaco.languages.CompletionItem[] {
8192
const keywords = [
8293
'SELECT',
@@ -115,19 +126,15 @@ export abstract class IntellisenseProvider {
115126
insertText: keyword,
116127
detail: 'SQL Keyword',
117128
sortText: '0' + keyword,
118-
range: {
119-
startLineNumber: position.lineNumber,
120-
startColumn: position.column,
121-
endLineNumber: position.lineNumber,
122-
endColumn: position.column
123-
}
129+
range
124130
}))
125131
}
126132

127133
protected getTableSuggestions(
128134
_context: SQLContext,
129135
schema: DatabaseSchema,
130-
position: Position
136+
position: Position,
137+
range: any
131138
): Monaco.languages.CompletionItem[] {
132139
const suggestions: Monaco.languages.CompletionItem[] = []
133140

@@ -143,12 +150,7 @@ export abstract class IntellisenseProvider {
143150
detail: `Table in ${database}`,
144151
documentation: `Columns: ${table.columns.map((c) => c.name).join(', ')}`,
145152
sortText: '1' + tableName,
146-
range: {
147-
startLineNumber: position.lineNumber,
148-
startColumn: position.column,
149-
endLineNumber: position.lineNumber,
150-
endColumn: position.column
151-
}
153+
range
152154
})
153155

154156
if (database !== this.options.currentDatabase) {
@@ -175,7 +177,8 @@ export abstract class IntellisenseProvider {
175177
protected getFunctionSuggestions(
176178
_context: SQLContext,
177179
_schema: DatabaseSchema,
178-
position: Position
180+
position: Position,
181+
range: any
179182
): Monaco.languages.CompletionItem[] {
180183
const functions = [
181184
{ name: 'COUNT', signature: 'COUNT(expression)', description: 'Returns the number of rows' },
@@ -207,19 +210,15 @@ export abstract class IntellisenseProvider {
207210
detail: 'SQL Function',
208211
documentation: func.description,
209212
sortText: '3' + func.name,
210-
range: {
211-
startLineNumber: position.lineNumber,
212-
startColumn: position.column,
213-
endLineNumber: position.lineNumber,
214-
endColumn: position.column
215-
}
213+
range
216214
}))
217215
}
218216

219217
protected getGeneralSuggestions(
220218
context: SQLContext,
221219
schema: DatabaseSchema,
222-
position: Position
220+
position: Position,
221+
range: any
223222
): Monaco.languages.CompletionItem[] {
224223
const suggestions: Monaco.languages.CompletionItem[] = []
225224

src/renderer/lib/intellisense/adapters/ClickHouseIntellisenseProvider.ts

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,8 @@ export class ClickHouseIntellisenseProvider extends IntellisenseProvider {
149149
getColumnSuggestions(
150150
context: SQLContext,
151151
schema: DatabaseSchema,
152-
position: Position
152+
position: Position,
153+
range: any
153154
): Monaco.languages.CompletionItem[] {
154155
const suggestions: Monaco.languages.CompletionItem[] = []
155156
const processedColumns = new Set<string>()
@@ -169,12 +170,7 @@ export class ClickHouseIntellisenseProvider extends IntellisenseProvider {
169170
detail: `${column.type}${column.nullable ? ' (nullable)' : ' NOT NULL'}`,
170171
documentation: `Column from ${tableName}`,
171172
sortText: '4' + column.name,
172-
range: {
173-
startLineNumber: position.lineNumber,
174-
startColumn: position.column,
175-
endLineNumber: position.lineNumber,
176-
endColumn: position.column
177-
}
173+
range
178174
})
179175
processedColumns.add(column.name)
180176
}
@@ -210,12 +206,7 @@ export class ClickHouseIntellisenseProvider extends IntellisenseProvider {
210206
detail: column.type,
211207
documentation: `Column from ${tableName} (aliased as ${alias})`,
212208
sortText: '6' + aliasedName,
213-
range: {
214-
startLineNumber: position.lineNumber,
215-
startColumn: position.column,
216-
endLineNumber: position.lineNumber,
217-
endColumn: position.column
218-
}
209+
range
219210
})
220211
})
221212
}

0 commit comments

Comments
 (0)