more codemirror enhancements (#2912)

Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
Jess Frazelle
2024-07-03 22:06:52 -07:00
committed by GitHub
parent 7cec1d45fe
commit 08e9fe2e52
5 changed files with 113 additions and 116 deletions

View File

@ -1455,8 +1455,6 @@ test.describe('Can create sketches on all planes and their back sides', () => {
}) })
test.describe('Copilot ghost text', () => { test.describe('Copilot ghost text', () => {
test.skip(true, 'temporarily disabled')
test('completes code in empty file', async ({ page }) => { test('completes code in empty file', async ({ page }) => {
const u = await getUtils(page) const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio // const PUR = 400 / 37.5 //pixeltoUnitRatio
@ -3715,16 +3713,14 @@ test.describe('Regression tests', () => {
// Make sure it's not a link // Make sure it's not a link
await expect(zooLogo).not.toHaveAttribute('href') await expect(zooLogo).not.toHaveAttribute('href')
}) })
test.fixme( test('Position _ Is Out Of Range... regression test', async ({ page }) => {
'Position _ Is Out Of Range... regression test', const u = await getUtils(page)
async ({ page }) => { // const PUR = 400 / 37.5 //pixeltoUnitRatio
const u = await getUtils(page) await page.setViewportSize({ width: 1200, height: 500 })
// const PUR = 400 / 37.5 //pixeltoUnitRatio await page.addInitScript(async () => {
await page.setViewportSize({ width: 1200, height: 500 }) localStorage.setItem(
await page.addInitScript(async () => { 'persistCode',
localStorage.setItem( `const exampleSketch = startSketchOn("XZ")
'persistCode',
`const exampleSketch = startSketchOn("XZ")
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ angle: 50, length: 45 }, %) |> angledLine({ angle: 50, length: 45 }, %)
|> yLineTo(0, %) |> yLineTo(0, %)
@ -3733,51 +3729,51 @@ test.describe('Regression tests', () => {
const example = extrude(5, exampleSketch) const example = extrude(5, exampleSketch)
shell({ faces: ['end'], thickness: 0.25 }, exampleSketch)` shell({ faces: ['end'], thickness: 0.25 }, exampleSketch)`
) )
}) })
await u.waitForAuthSkipAppStart() await u.waitForAuthSkipAppStart()
// error in guter // error in guter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible() await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
// error text on hover // error text on hover
await page.hover('.cm-lint-marker-error') await page.hover('.cm-lint-marker-error')
await expect(page.getByText('Unexpected token').first()).toBeVisible() await expect(page.getByText('Unexpected token').first()).toBeVisible()
// Okay execution finished, let's start editing text below the error. // Okay execution finished, let's start editing text below the error.
await u.codeLocator.click() await u.codeLocator.click()
// Go to the end of the editor // Go to the end of the editor
// This bug happens when there is a diagnostic in the editor and you try to // This bug happens when there is a diagnostic in the editor and you try to
// edit text below it. // edit text below it.
// Or delete a huge chunk of text and then try to edit below it. // Or delete a huge chunk of text and then try to edit below it.
await page.keyboard.press('End') await page.keyboard.press('End')
await page.keyboard.down('Shift') await page.keyboard.down('Shift')
await page.keyboard.press('ArrowUp') await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp') await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp') await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp') await page.keyboard.press('ArrowUp')
await page.keyboard.up('Shift') await page.keyboard.press('ArrowUp')
await page.keyboard.press('Backspace') await page.keyboard.up('Shift')
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() await page.keyboard.press('Backspace')
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
await page.keyboard.type('thing: "blah"', { delay: 100 }) await page.keyboard.type('thing: "blah"', { delay: 100 })
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`const exampleSketch = startSketchOn("XZ") .toContainText(`const exampleSketch = startSketchOn("XZ")
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine({ angle: 50, length: 45 }, %) |> angledLine({ angle: 50, length: 45 }, %)
|> yLineTo(0, %) |> yLineTo(0, %)
|> close(%) |> close(%
thing: "blah"`) thing: "blah"`)
await expect(page.locator('.cm-lint-marker-error')).toBeVisible() await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
} })
)
}) })
test.describe('Sketch tests', () => { test.describe('Sketch tests', () => {

View File

@ -1,10 +1,7 @@
import { autocompletion } from '@codemirror/autocomplete' import { foldService } from '@codemirror/language'
import { foldService, syntaxTree } from '@codemirror/language'
import { Extension, EditorState } from '@codemirror/state' import { Extension, EditorState } from '@codemirror/state'
import { ViewPlugin } from '@codemirror/view' import { ViewPlugin } from '@codemirror/view'
import { CompletionTriggerKind } from 'vscode-languageserver-protocol'
import { import {
docPathFacet, docPathFacet,
LanguageServerPlugin, LanguageServerPlugin,
@ -13,7 +10,6 @@ import {
workspaceFolders, workspaceFolders,
LanguageServerOptions, LanguageServerOptions,
} from './plugin/lsp' } from './plugin/lsp'
import { offsetToPos } from './plugin/util'
export type { LanguageServerClientOptions } from './client' export type { LanguageServerClientOptions } from './client'
export { LanguageServerClient } from './client' export { LanguageServerClient } from './client'
@ -57,59 +53,5 @@ export function lspPlugin(options: LanguageServerOptions): Extension {
}), }),
] ]
if (options.client.getServerCapabilities().completionProvider) {
ext.push(
autocompletion({
defaultKeymap: false,
override: [
async (context) => {
if (plugin === null) {
return null
}
const { state, pos, explicit } = context
let nodeBefore = syntaxTree(state).resolveInner(pos, -1)
if (
nodeBefore.name === 'BlockComment' ||
nodeBefore.name === 'LineComment'
)
return null
const line = state.doc.lineAt(pos)
let trigKind: CompletionTriggerKind = CompletionTriggerKind.Invoked
let trigChar: string | undefined
if (
!explicit &&
plugin.client
.getServerCapabilities()
.completionProvider?.triggerCharacters?.includes(
line.text[pos - line.from - 1]
)
) {
trigKind = CompletionTriggerKind.TriggerCharacter
trigChar = line.text[pos - line.from - 1]
}
if (
trigKind === CompletionTriggerKind.Invoked &&
!context.matchBefore(/\w+$/)
) {
return null
}
return await plugin.requestCompletion(
context,
offsetToPos(state.doc, pos),
{
triggerKind: trigKind,
triggerCharacter: trigChar,
}
)
},
],
})
)
}
return ext return ext
} }

View File

@ -1,5 +1,6 @@
import { import {
acceptCompletion, acceptCompletion,
autocompletion,
clearSnippet, clearSnippet,
closeCompletion, closeCompletion,
hasNextSnippetField, hasNextSnippetField,
@ -8,10 +9,17 @@ import {
prevSnippetField, prevSnippetField,
startCompletion, startCompletion,
} from '@codemirror/autocomplete' } from '@codemirror/autocomplete'
import { Prec } from '@codemirror/state' import { Prec, Extension } from '@codemirror/state'
import { EditorView, keymap, KeyBinding } from '@codemirror/view' import { EditorView, keymap, KeyBinding, ViewPlugin } from '@codemirror/view'
import { CompletionItemKind } from 'vscode-languageserver-protocol' import {
CompletionItemKind,
CompletionTriggerKind,
} from 'vscode-languageserver-protocol'
import { LanguageServerPlugin } from './lsp'
import { offsetToPos } from './util'
import { syntaxTree } from '@codemirror/language'
export const CompletionItemKindMap = Object.fromEntries( export const CompletionItemKindMap = Object.fromEntries(
Object.entries(CompletionItemKind).map(([key, value]) => [value, key]) Object.entries(CompletionItemKind).map(([key, value]) => [value, key])
@ -46,6 +54,59 @@ const lspAutocompleteKeymap: readonly KeyBinding[] = [
}, },
] ]
export const lspAutocompleteKeymapExt = Prec.highest( const lspAutocompleteKeymapExt = Prec.highest(keymap.of(lspAutocompleteKeymap))
keymap.of(lspAutocompleteKeymap)
) export default function lspAutocompleteExt(
plugin: ViewPlugin<LanguageServerPlugin>
): Extension {
return [
lspAutocompleteKeymapExt,
autocompletion({
defaultKeymap: false,
override: [
async (context) => {
const { state, pos, explicit, view } = context
let value = view?.plugin(plugin)
if (!value) return null
let nodeBefore = syntaxTree(state).resolveInner(pos, -1)
if (
nodeBefore.name === 'BlockComment' ||
nodeBefore.name === 'LineComment'
)
return null
const line = state.doc.lineAt(pos)
let trigKind: CompletionTriggerKind = CompletionTriggerKind.Invoked
let trigChar: string | undefined
if (
!explicit &&
value.client
.getServerCapabilities()
.completionProvider?.triggerCharacters?.includes(
line.text[pos - line.from - 1]
)
) {
trigKind = CompletionTriggerKind.TriggerCharacter
trigChar = line.text[pos - line.from - 1]
}
if (
trigKind === CompletionTriggerKind.Invoked &&
!context.matchBefore(/\w+$/)
) {
return null
}
return await value.requestCompletion(
context,
offsetToPos(state.doc, pos),
{
triggerKind: trigKind,
triggerCharacter: trigChar,
}
)
},
],
}),
]
}

View File

@ -31,7 +31,7 @@ import { LanguageServerClient } from '../client'
import { CompletionItemKindMap } from './autocomplete' import { CompletionItemKindMap } from './autocomplete'
import { addToken, SemanticToken } from './semantic-tokens' import { addToken, SemanticToken } from './semantic-tokens'
import { deferExecution, posToOffset, formatMarkdownContents } from './util' import { deferExecution, posToOffset, formatMarkdownContents } from './util'
import { lspAutocompleteKeymapExt } from './autocomplete' import lspAutocompleteExt from './autocomplete'
import lspHoverExt from './hover' import lspHoverExt from './hover'
import lspFormatExt from './format' import lspFormatExt from './format'
import lspIndentExt from './indent' import lspIndentExt from './indent'
@ -556,7 +556,7 @@ export class LanguageServerPluginSpec
{ {
provide(plugin: ViewPlugin<LanguageServerPlugin>): Extension { provide(plugin: ViewPlugin<LanguageServerPlugin>): Extension {
return [ return [
lspAutocompleteKeymapExt, lspAutocompleteExt(plugin),
lspFormatExt(plugin), lspFormatExt(plugin),
lspHoverExt(plugin), lspHoverExt(plugin),
lspIndentExt(), lspIndentExt(),

View File

@ -29,7 +29,6 @@ import {
LanguageServerClient, LanguageServerClient,
docPathFacet, docPathFacet,
languageId, languageId,
lspPlugin,
} from '@kittycad/codemirror-lsp-client' } from '@kittycad/codemirror-lsp-client'
import { deferExecution } from 'lib/utils' import { deferExecution } from 'lib/utils'
import { CopilotLspCompletionParams } from 'wasm-lib/kcl/bindings/CopilotLspCompletionParams' import { CopilotLspCompletionParams } from 'wasm-lib/kcl/bindings/CopilotLspCompletionParams'
@ -621,7 +620,6 @@ export const copilotPlugin = (options: LanguageServerOptions): Extension => {
) )
return [ return [
/*lspPlugin(options),
completionPlugin, completionPlugin,
copilotAutocompleteKeymapExt, copilotAutocompleteKeymapExt,
domHandlers, domHandlers,
@ -632,6 +630,6 @@ export const copilotPlugin = (options: LanguageServerOptions): Extension => {
plugin.rejectSuggestionCommand() plugin.rejectSuggestionCommand()
return null return null
}),*/ }),
] ]
} }