Compare commits
3 Commits
v1.0.5
...
marijnh/si
Author | SHA1 | Date | |
---|---|---|---|
aa0b97de3d | |||
24a241c05a | |||
9265af9115 |
@ -15,10 +15,6 @@ export default function lspGoToDefinitionExt(
|
|||||||
{
|
{
|
||||||
key: 'F12',
|
key: 'F12',
|
||||||
run: (view) => {
|
run: (view) => {
|
||||||
if (!plugin) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const value = view.plugin(plugin)
|
const value = view.plugin(plugin)
|
||||||
if (!value) return false
|
if (!value) return false
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ import {
|
|||||||
import { isArray } from '../lib/utils'
|
import { isArray } from '../lib/utils'
|
||||||
import lspGoToDefinitionExt from './go-to-definition'
|
import lspGoToDefinitionExt from './go-to-definition'
|
||||||
import lspRenameExt from './rename'
|
import lspRenameExt from './rename'
|
||||||
import lspSignatureHelpExt from './signature-help'
|
import { lspSignatureHelpExt, setSignatureTooltip } from './signature-help'
|
||||||
|
|
||||||
const useLast = (values: readonly any[]) => values.reduce((_, v) => v, '')
|
const useLast = (values: readonly any[]) => values.reduce((_, v) => v, '')
|
||||||
export const docPathFacet = Facet.define<string, string>({
|
export const docPathFacet = Facet.define<string, string>({
|
||||||
@ -695,6 +695,8 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
|
|
||||||
// Create the tooltip container
|
// Create the tooltip container
|
||||||
const dom = this.createTooltipContainer()
|
const dom = this.createTooltipContainer()
|
||||||
|
dom.className =
|
||||||
|
'documentation hover-tooltip cm-tooltip cm-signature-tooltip'
|
||||||
|
|
||||||
// Get active signature
|
// Get active signature
|
||||||
const activeSignatureIndex = result.activeSignature ?? 0
|
const activeSignatureIndex = result.activeSignature ?? 0
|
||||||
@ -769,42 +771,7 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (tooltip) {
|
if (tooltip) {
|
||||||
// Create and show the tooltip manually
|
view.dispatch({ effects: setSignatureTooltip.of(tooltip) })
|
||||||
const { pos: tooltipPos, create } = tooltip
|
|
||||||
const tooltipView = create(view)
|
|
||||||
|
|
||||||
const tooltipElement = document.createElement('div')
|
|
||||||
tooltipElement.className =
|
|
||||||
'documentation hover-tooltip cm-tooltip cm-signature-tooltip'
|
|
||||||
tooltipElement.style.position = 'absolute'
|
|
||||||
tooltipElement.style.zIndex = '99999999'
|
|
||||||
|
|
||||||
tooltipElement.appendChild(tooltipView.dom)
|
|
||||||
|
|
||||||
// Position the tooltip
|
|
||||||
const coords = view.coordsAtPos(tooltipPos)
|
|
||||||
if (coords) {
|
|
||||||
tooltipElement.style.left = `${coords.left}px`
|
|
||||||
tooltipElement.style.top = `${coords.bottom + 5}px`
|
|
||||||
|
|
||||||
// Add to DOM
|
|
||||||
document.body.appendChild(tooltipElement)
|
|
||||||
|
|
||||||
// Remove after a delay or on editor changes
|
|
||||||
setTimeout(() => {
|
|
||||||
removeTooltip() // Use the function that also cleans up event listeners
|
|
||||||
}, 10000) // Show for 10 seconds
|
|
||||||
|
|
||||||
// Also remove on any user input
|
|
||||||
const removeTooltip = () => {
|
|
||||||
tooltipElement.remove()
|
|
||||||
view.dom.removeEventListener('keydown', removeTooltip)
|
|
||||||
view.dom.removeEventListener('mousedown', removeTooltip)
|
|
||||||
}
|
|
||||||
|
|
||||||
view.dom.addEventListener('keydown', removeTooltip)
|
|
||||||
view.dom.addEventListener('mousedown', removeTooltip)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1034,15 +1001,8 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort edits in reverse order to avoid position shifts
|
|
||||||
const sortedEdits = docChange.edits.sort((a, b) => {
|
|
||||||
const posA = posToOffset(view.state.doc, a.range.start)
|
|
||||||
const posB = posToOffset(view.state.doc, b.range.start)
|
|
||||||
return (posB ?? 0) - (posA ?? 0)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Create a single transaction with all changes
|
// Create a single transaction with all changes
|
||||||
const changes = sortedEdits.map((edit) => ({
|
const changes = docChange.edits.map((edit) => ({
|
||||||
from: posToOffset(view.state.doc, edit.range.start) ?? 0,
|
from: posToOffset(view.state.doc, edit.range.start) ?? 0,
|
||||||
to: posToOffset(view.state.doc, edit.range.end) ?? 0,
|
to: posToOffset(view.state.doc, edit.range.end) ?? 0,
|
||||||
insert: edit.newText,
|
insert: edit.newText,
|
||||||
@ -1070,15 +1030,8 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort changes in reverse order to avoid position shifts
|
|
||||||
const sortedChanges = changes.sort((a, b) => {
|
|
||||||
const posA = posToOffset(view.state.doc, a.range.start)
|
|
||||||
const posB = posToOffset(view.state.doc, b.range.start)
|
|
||||||
return (posB ?? 0) - (posA ?? 0)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Create a single transaction with all changes
|
// Create a single transaction with all changes
|
||||||
const changeSpecs = sortedChanges.map((change) => ({
|
const changeSpecs = changes.map((change) => ({
|
||||||
from: posToOffset(view.state.doc, change.range.start) ?? 0,
|
from: posToOffset(view.state.doc, change.range.start) ?? 0,
|
||||||
to: posToOffset(view.state.doc, change.range.end) ?? 0,
|
to: posToOffset(view.state.doc, change.range.end) ?? 0,
|
||||||
insert: change.newText,
|
insert: change.newText,
|
||||||
|
@ -15,8 +15,6 @@ export default function lspRenameExt(
|
|||||||
{
|
{
|
||||||
key: 'F2',
|
key: 'F2',
|
||||||
run: (view) => {
|
run: (view) => {
|
||||||
if (!plugin) return false
|
|
||||||
|
|
||||||
const value = view.plugin(plugin)
|
const value = view.plugin(plugin)
|
||||||
if (!value) return false
|
if (!value) return false
|
||||||
|
|
||||||
|
@ -1,12 +1,68 @@
|
|||||||
import type { Extension } from '@codemirror/state'
|
import {
|
||||||
import { Prec } from '@codemirror/state'
|
type EditorState,
|
||||||
import type { ViewPlugin } from '@codemirror/view'
|
type Extension,
|
||||||
import { EditorView } from '@codemirror/view'
|
Prec,
|
||||||
import { keymap } from '@codemirror/view'
|
StateEffect,
|
||||||
|
StateField,
|
||||||
|
} from '@codemirror/state'
|
||||||
|
import type { Tooltip } from '@codemirror/view'
|
||||||
|
import { type ViewPlugin, showTooltip } from '@codemirror/view'
|
||||||
|
import { EditorView, keymap } from '@codemirror/view'
|
||||||
|
import { syntaxTree } from '@codemirror/language'
|
||||||
|
import { type SyntaxNode } from '@lezer/common'
|
||||||
|
|
||||||
import type { LanguageServerPlugin } from './lsp'
|
import type { LanguageServerPlugin } from './lsp'
|
||||||
|
|
||||||
export default function lspSignatureHelpExt(
|
export const setSignatureTooltip = StateEffect.define<Tooltip | null>()
|
||||||
|
|
||||||
|
function findParenthesized(
|
||||||
|
state: EditorState,
|
||||||
|
pos: number,
|
||||||
|
side: 1 | 0 | -1 = 0
|
||||||
|
) {
|
||||||
|
let context: SyntaxNode | null = syntaxTree(state).resolveInner(pos, side)
|
||||||
|
while (context) {
|
||||||
|
const open = context.firstChild
|
||||||
|
if (
|
||||||
|
open &&
|
||||||
|
open.from == context.from &&
|
||||||
|
open.to == context.from + 1 &&
|
||||||
|
state.doc.sliceString(open.from, open.to) == '('
|
||||||
|
)
|
||||||
|
break
|
||||||
|
context = context.parent
|
||||||
|
}
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
const signatureTooltip = StateField.define<Tooltip | null>({
|
||||||
|
create: () => null,
|
||||||
|
update(value, tr) {
|
||||||
|
for (let effect of tr.effects) {
|
||||||
|
if (effect.is(setSignatureTooltip)) return effect.value
|
||||||
|
}
|
||||||
|
if (!value) return null
|
||||||
|
if (tr.selection) {
|
||||||
|
let parens = findParenthesized(tr.state, tr.selection.main.head)
|
||||||
|
if (!parens || parens.from != value.pos) return null
|
||||||
|
}
|
||||||
|
return tr.docChanged
|
||||||
|
? { ...value, pos: tr.changes.mapPos(value.pos) }
|
||||||
|
: value
|
||||||
|
},
|
||||||
|
provide: (f) => [
|
||||||
|
showTooltip.from(f),
|
||||||
|
EditorView.domEventHandlers({
|
||||||
|
blur: (_, view) => {
|
||||||
|
if (view.state.field(f)) {
|
||||||
|
view.dispatch({ effects: setSignatureTooltip.of(null) })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
export function lspSignatureHelpExt(
|
||||||
plugin: ViewPlugin<LanguageServerPlugin>
|
plugin: ViewPlugin<LanguageServerPlugin>
|
||||||
): Extension {
|
): Extension {
|
||||||
return [
|
return [
|
||||||
@ -15,16 +71,16 @@ export default function lspSignatureHelpExt(
|
|||||||
{
|
{
|
||||||
key: 'Mod-Shift-Space',
|
key: 'Mod-Shift-Space',
|
||||||
run: (view) => {
|
run: (view) => {
|
||||||
if (!plugin) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const value = view.plugin(plugin)
|
const value = view.plugin(plugin)
|
||||||
if (!value) return false
|
if (!value) return false
|
||||||
|
|
||||||
const pos = view.state.selection.main.head
|
const parens = findParenthesized(
|
||||||
|
view.state,
|
||||||
|
view.state.selection.main.head
|
||||||
|
)
|
||||||
|
if (!parens) return false
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
value.showSignatureHelpTooltip(view, pos)
|
value.showSignatureHelpTooltip(view, parens.from)
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -32,17 +88,10 @@ export default function lspSignatureHelpExt(
|
|||||||
),
|
),
|
||||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
EditorView.updateListener.of(async (update) => {
|
EditorView.updateListener.of(async (update) => {
|
||||||
if (!(plugin && update.docChanged)) return
|
if (!update.docChanged) return
|
||||||
|
|
||||||
// Make sure this is a valid user typing event.
|
// Make sure this is a valid user typing event.
|
||||||
let isRelevant = false
|
if (!update.transactions.some((tr) => tr.isUserEvent('input'))) {
|
||||||
for (const tr of update.transactions) {
|
|
||||||
if (tr.isUserEvent('input')) {
|
|
||||||
isRelevant = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isRelevant) {
|
|
||||||
// We only want signature help on user events.
|
// We only want signature help on user events.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -59,18 +108,16 @@ export default function lspSignatureHelpExt(
|
|||||||
|
|
||||||
// Check if changes include trigger characters
|
// Check if changes include trigger characters
|
||||||
const changes = update.changes
|
const changes = update.changes
|
||||||
let shouldTrigger = false
|
|
||||||
let triggerPos = -1
|
let triggerPos = -1
|
||||||
|
|
||||||
changes.iterChanges((_fromA, _toA, _fromB, toB, inserted) => {
|
changes.iterChanges((_fromA, _toA, _fromB, toB, inserted) => {
|
||||||
if (shouldTrigger) return // Skip if already found a trigger
|
if (triggerPos >= 0) return // Skip if already found a trigger
|
||||||
|
|
||||||
const text = inserted.toString()
|
const text = inserted.toString()
|
||||||
if (!text) return
|
if (!text) return
|
||||||
|
|
||||||
for (const char of triggerChars) {
|
for (const char of triggerChars) {
|
||||||
if (text.includes(char)) {
|
if (text.includes(char)) {
|
||||||
shouldTrigger = true
|
|
||||||
triggerPos = toB
|
triggerPos = toB
|
||||||
triggerCharacter = char
|
triggerCharacter = char
|
||||||
break
|
break
|
||||||
@ -78,13 +125,17 @@ export default function lspSignatureHelpExt(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (shouldTrigger && triggerPos >= 0) {
|
if (triggerPos >= 0) {
|
||||||
await value.showSignatureHelpTooltip(
|
const parens = findParenthesized(update.view.state, triggerPos, -1)
|
||||||
update.view,
|
if (parens) {
|
||||||
triggerPos,
|
await value.showSignatureHelpTooltip(
|
||||||
triggerCharacter
|
update.view,
|
||||||
)
|
parens.from,
|
||||||
|
triggerCharacter
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
signatureTooltip,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user