Skip to content

Commit abd3570

Browse files
committed
optimize code editor insertion
1 parent b6ceff3 commit abd3570

File tree

5 files changed

+103
-11
lines changed

5 files changed

+103
-11
lines changed

spx-gui/src/components/editor/code-editor/code-editor.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ import {
5454
type CommandArgs,
5555
getTextDocumentId,
5656
containsPosition,
57-
makeBasicMarkdownString,
5857
type WorkspaceDiagnostics,
5958
type TextDocumentDiagnostics,
6059
fromLSPDiagnostic
@@ -273,6 +272,11 @@ class CompletionProvider implements ICompletionProvider {
273272
documentation: null
274273
}
275274

275+
if (item.insertText != null) {
276+
result.insertText = item.insertText
277+
result.insertTextFormat = this.getInsertTextFormat(item.insertTextFormat)
278+
}
279+
276280
const defId = item.data?.definition
277281
const definition = defId != null ? await this.documentBase.getDocumentation(defId) : null
278282

@@ -284,18 +288,14 @@ class CompletionProvider implements ICompletionProvider {
284288
result.kind = definition.kind
285289
result.insertText = definition.insertText
286290
result.insertTextFormat = InsertTextFormat.Snippet
287-
result.documentation = makeBasicMarkdownString(definition.overview)
291+
result.documentation = definition.detail
288292
}
289293

290294
if (item.documentation != null) {
291295
const docStr = lsp.MarkupContent.is(item.documentation) ? item.documentation.value : item.documentation
292296
result.documentation = makeAdvancedMarkdownString(docStr)
293297
}
294298

295-
if (item.insertText != null) {
296-
result.insertText = item.insertText
297-
result.insertTextFormat = this.getInsertTextFormat(item.insertTextFormat)
298-
}
299299
return result
300300
})
301301
)

spx-gui/src/components/editor/code-editor/text-document.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ export class TextDocument
173173
return this.monacoTextModel.getValueInRange(toMonacoRange(range))
174174
}
175175

176+
getLineContent(line: number): string {
177+
return this.monacoTextModel.getLineContent(line)
178+
}
179+
176180
getWordAtPosition(position: Position): WordAtPosition | null {
177181
return this.monacoTextModel.getWordAtPosition(toMonacoPosition(position))
178182
}

spx-gui/src/components/editor/code-editor/ui/api-reference/APIReferenceItem.vue

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<script setup lang="ts">
22
import { useMessageHandle } from '@/utils/exception'
33
import { UITooltip } from '@/components/ui'
4+
import { DefinitionKind } from '../../common'
45
import DefinitionOverviewWrapper from '../definition/DefinitionOverviewWrapper.vue'
56
import DefinitionDetailWrapper from '../definition/DefinitionDetailWrapper.vue'
67
import MarkdownView from '../markdown/MarkdownView.vue'
@@ -14,10 +15,21 @@ const props = defineProps<{
1415
1516
const codeEditorCtx = useCodeEditorUICtx()
1617
17-
const handleInsert = useMessageHandle(() => codeEditorCtx.ui.insertSnippet(props.item.insertText), {
18-
en: 'Failed to insert',
19-
zh: '插入失败'
20-
}).fn
18+
const blockDefinitionKinds = [DefinitionKind.Command, DefinitionKind.Listen, DefinitionKind.Statement]
19+
20+
const handleInsert = useMessageHandle(
21+
() => {
22+
if (blockDefinitionKinds.includes(props.item.kind)) {
23+
codeEditorCtx.ui.insertBlockSnippet(props.item.insertText)
24+
} else {
25+
codeEditorCtx.ui.insertInlineSnippet(props.item.insertText)
26+
}
27+
},
28+
{
29+
en: 'Failed to insert',
30+
zh: '插入失败'
31+
}
32+
).fn
2133
2234
const handleExplain = useMessageHandle(
2335
() =>

spx-gui/src/components/editor/code-editor/ui/code-editor-ui.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,78 @@ export class CodeEditorUI extends Disposable implements ICodeEditorUI {
282282
this.editor.focus()
283283
}
284284

285+
private ensureEOLLast(textDocument: TextDocument, position: Position) {
286+
const offset = textDocument.getOffsetAt(position)
287+
const value = textDocument.getValue()
288+
if (offset === value.length && value[offset - 1] !== '\n') {
289+
this.insertText('\n', { start: position, end: position })
290+
}
291+
}
292+
293+
private async insertInlineContent(
294+
content: string,
295+
range: Range = this.getSelectionRange(),
296+
insertFn: typeof this.insertText
297+
) {
298+
const textDocument = this.activeTextDocument
299+
if (textDocument == null) return
300+
const insertContent = async (cnt: string, rg: Range) => {
301+
await insertFn(cnt, rg)
302+
this.ensureEOLLast(textDocument, rg.end)
303+
}
304+
if (!isRangeEmpty(range)) return insertContent(content, range)
305+
const position = range.start
306+
const line = textDocument.getLineContent(position.line)
307+
if (isEmptyText(line)) return insertContent(content, range)
308+
const word = textDocument.getWordAtPosition(position)
309+
if (word == null) return insertContent(content, range)
310+
const isPositionInWord = position.column >= word.startColumn && position.column <= word.endColumn
311+
if (isPositionInWord) {
312+
const wordEnd = { line: position.line, column: word.endColumn }
313+
this.editor.setPosition(toMonacoPosition(wordEnd))
314+
return insertContent(' ' + content, { start: wordEnd, end: wordEnd })
315+
}
316+
return insertContent(content, range)
317+
}
318+
319+
private async insertBlockContent(
320+
content: string,
321+
range: Range = this.getSelectionRange(),
322+
insertFn: typeof this.insertText
323+
) {
324+
const textDocument = this.activeTextDocument
325+
if (textDocument == null) return
326+
const insertContent = async (cnt: string, rg: Range) => {
327+
await insertFn(cnt, rg)
328+
this.ensureEOLLast(textDocument, rg.end)
329+
}
330+
let position = range.end
331+
const line = textDocument.getLineContent(position.line)
332+
if (isEmptyText(line)) return insertContent(content, range)
333+
const lineHead = { line: position.line, column: 1 }
334+
const beforePosition = textDocument.getValueInRange({ start: lineHead, end: position })
335+
if (isEmptyText(beforePosition)) return insertContent(content + '\n', range)
336+
position = { line: position.line, column: line.length + 1 }
337+
this.editor.setPosition(toMonacoPosition(position))
338+
return insertContent('\n' + content, { start: position, end: position }) // proper indentation needed here
339+
}
340+
341+
async insertInlineText(text: string, range: Range = this.getSelectionRange()) {
342+
return this.insertInlineContent(text, range, this.insertText.bind(this))
343+
}
344+
345+
async insertInlineSnippet(snippet: string, range: Range = this.getSelectionRange()) {
346+
return this.insertInlineContent(snippet, range, this.insertSnippet.bind(this))
347+
}
348+
349+
async insertBlockText(text: string, range: Range = this.getSelectionRange()) {
350+
return this.insertBlockContent(text, range, this.insertText.bind(this))
351+
}
352+
353+
async insertBlockSnippet(snippet: string, range: Range = this.getSelectionRange()) {
354+
return this.insertBlockContent(snippet, range, this.insertSnippet.bind(this))
355+
}
356+
285357
private cursorPositionRef = shallowRef<Position | null>(null)
286358
/** Cursor position (in current active text document) */
287359
get cursorPosition() {
@@ -478,3 +550,7 @@ export class CodeEditorUI extends Disposable implements ICodeEditorUI {
478550
super.dispose()
479551
}
480552
}
553+
554+
function isEmptyText(s: string) {
555+
return /^\s*$/.test(s)
556+
}

spx-gui/src/components/editor/code-editor/ui/markdown/CodeBlock.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const code = useSlotText()
1919
const handleInsert = useMessageHandle(
2020
() =>
2121
editorCtx.project.history.doAction({ name: { en: 'Insert code', zh: '插入代码' } }, () =>
22-
codeEditorUICtx.ui.insertText(code.value)
22+
codeEditorUICtx.ui.insertBlockText(code.value)
2323
),
2424
{ en: 'Failed to insert code', zh: '插入代码失败' }
2525
).fn

0 commit comments

Comments
 (0)