cleanup annotations, makes it easier to read (#2905)

ckeanup annotations

Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
Jess Frazelle
2024-07-03 20:59:54 -07:00
committed by GitHub
parent 8be113d284
commit 87e7e9447f
6 changed files with 72 additions and 220 deletions

View File

@ -24,14 +24,15 @@ export {
LspWorkerEventType, LspWorkerEventType,
} from './client/codec' } from './client/codec'
export type { LanguageServerOptions } from './plugin/lsp' export type { LanguageServerOptions } from './plugin/lsp'
export type { TransactionInfo, RelevantUpdate } from './plugin/annotations'
export { updateInfo, TransactionAnnotation } from './plugin/annotations'
export { export {
LanguageServerPlugin, LanguageServerPlugin,
LanguageServerPluginSpec, LanguageServerPluginSpec,
docPathFacet, docPathFacet,
languageId, languageId,
workspaceFolders, workspaceFolders,
lspSemanticTokensEvent,
lspDiagnosticsEvent,
lspFormatCodeEvent,
} from './plugin/lsp' } from './plugin/lsp'
export { posToOffset, offsetToPos } from './plugin/util' export { posToOffset, offsetToPos } from './plugin/util'

View File

@ -1,131 +0,0 @@
import { hasNextSnippetField, pickedCompletion } from '@codemirror/autocomplete'
import { Annotation, Transaction } from '@codemirror/state'
import type { ViewUpdate } from '@codemirror/view'
export enum LspAnnotation {
SemanticTokens = 'semantic-tokens',
FormatCode = 'format-code',
Diagnostics = 'diagnostics',
}
const lspEvent = Annotation.define<LspAnnotation>()
export const lspSemanticTokensEvent = lspEvent.of(LspAnnotation.SemanticTokens)
export const lspFormatCodeEvent = lspEvent.of(LspAnnotation.FormatCode)
export const lspDiagnosticsEvent = lspEvent.of(LspAnnotation.Diagnostics)
export enum TransactionAnnotation {
Remote = 'remote',
UserSelect = 'user.select',
UserInput = 'user.input',
UserMove = 'user.move',
UserDelete = 'user.delete',
UserUndo = 'user.undo',
UserRedo = 'user.redo',
SemanticTokens = 'SemanticTokens',
FormatCode = 'FormatCode',
Diagnostics = 'Diagnostics',
PickedCompletion = 'PickedCompletion',
}
export interface TransactionInfo {
annotations: TransactionAnnotation[]
time: number | null
docChanged: boolean
addToHistory: boolean
inSnippet: boolean
transaction: Transaction
}
export const updateInfo = (update: ViewUpdate): TransactionInfo[] => {
let transactionInfos: TransactionInfo[] = []
for (const tr of update.transactions) {
let annotations: TransactionAnnotation[] = []
if (tr.isUserEvent('select')) {
annotations.push(TransactionAnnotation.UserSelect)
}
if (tr.isUserEvent('input')) {
annotations.push(TransactionAnnotation.UserInput)
}
if (tr.isUserEvent('delete')) {
annotations.push(TransactionAnnotation.UserDelete)
}
if (tr.isUserEvent('undo')) {
annotations.push(TransactionAnnotation.UserUndo)
}
if (tr.isUserEvent('redo')) {
annotations.push(TransactionAnnotation.UserRedo)
}
if (tr.isUserEvent('move')) {
annotations.push(TransactionAnnotation.UserMove)
}
if (tr.annotation(pickedCompletion) !== undefined) {
annotations.push(TransactionAnnotation.PickedCompletion)
}
if (tr.annotation(lspSemanticTokensEvent.type) !== undefined) {
annotations.push(TransactionAnnotation.SemanticTokens)
}
if (tr.annotation(lspFormatCodeEvent.type) !== undefined) {
annotations.push(TransactionAnnotation.FormatCode)
}
if (tr.annotation(lspDiagnosticsEvent.type) !== undefined) {
annotations.push(TransactionAnnotation.Diagnostics)
}
if (tr.annotation(Transaction.remote) !== undefined) {
annotations.push(TransactionAnnotation.Remote)
}
transactionInfos.push({
annotations,
time: tr.annotation(Transaction.time) || null,
docChanged: tr.docChanged,
addToHistory: tr.annotation(Transaction.addToHistory) || false,
inSnippet: hasNextSnippetField(update.state),
transaction: tr,
})
}
return transactionInfos
}
export interface RelevantUpdate {
overall: boolean
userSelect: boolean
time: number | null
}
export const relevantUpdate = (update: ViewUpdate): RelevantUpdate => {
const infos = updateInfo(update)
// Make sure we are not in a snippet
if (infos.some((info) => info.inSnippet)) {
return {
overall: false,
userSelect: false,
time: null,
}
}
return {
overall: infos.some(
(info) =>
info.docChanged ||
info.annotations.includes(TransactionAnnotation.UserInput) ||
info.annotations.includes(TransactionAnnotation.UserDelete) ||
info.annotations.includes(TransactionAnnotation.UserUndo) ||
info.annotations.includes(TransactionAnnotation.UserRedo) ||
info.annotations.includes(TransactionAnnotation.UserMove)
),
userSelect: infos.some((info) =>
info.annotations.includes(TransactionAnnotation.UserSelect)
),
time: infos.length ? infos[0].time : null,
}
}

View File

@ -4,7 +4,13 @@ import type {
CompletionResult, CompletionResult,
} from '@codemirror/autocomplete' } from '@codemirror/autocomplete'
import { completeFromList, snippetCompletion } from '@codemirror/autocomplete' import { completeFromList, snippetCompletion } from '@codemirror/autocomplete'
import { Facet, StateEffect, Extension, Transaction } from '@codemirror/state' import {
Facet,
StateEffect,
Extension,
Transaction,
Annotation,
} from '@codemirror/state'
import type { import type {
ViewUpdate, ViewUpdate,
PluginValue, PluginValue,
@ -22,11 +28,6 @@ import {
import { URI } from 'vscode-uri' import { URI } from 'vscode-uri'
import { LanguageServerClient } from '../client' import { LanguageServerClient } from '../client'
import {
lspSemanticTokensEvent,
lspFormatCodeEvent,
relevantUpdate,
} from './annotations'
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'
@ -47,6 +48,17 @@ export const workspaceFolders = Facet.define<
LSP.WorkspaceFolder[] LSP.WorkspaceFolder[]
>({ combine: useLast }) >({ combine: useLast })
export enum LspAnnotation {
SemanticTokens = 'semantic-tokens',
FormatCode = 'format-code',
Diagnostics = 'diagnostics',
}
const lspEvent = Annotation.define<LspAnnotation>()
export const lspSemanticTokensEvent = lspEvent.of(LspAnnotation.SemanticTokens)
export const lspFormatCodeEvent = lspEvent.of(LspAnnotation.FormatCode)
export const lspDiagnosticsEvent = lspEvent.of(LspAnnotation.Diagnostics)
export interface LanguageServerOptions { export interface LanguageServerOptions {
// We assume this is the main project directory, we are currently working in. // We assume this is the main project directory, we are currently working in.
workspaceFolders: LSP.WorkspaceFolder[] workspaceFolders: LSP.WorkspaceFolder[]
@ -131,11 +143,6 @@ export class LanguageServerPlugin implements PluginValue {
} }
update(viewUpdate: ViewUpdate) { update(viewUpdate: ViewUpdate) {
const isRelevant = relevantUpdate(viewUpdate)
if (!isRelevant.overall) {
return
}
// If the doc didn't change we can return early. // If the doc didn't change we can return early.
if (!viewUpdate.docChanged) { if (!viewUpdate.docChanged) {
return return

View File

@ -4,7 +4,7 @@ import { EditorView, Decoration, DecorationSet } from '@codemirror/view'
import { Tag, tags } from '@lezer/highlight' import { Tag, tags } from '@lezer/highlight'
import { lspSemanticTokensEvent } from './annotations' import { lspSemanticTokensEvent } from './lsp'
export interface SemanticToken { export interface SemanticToken {
from: number from: number

View File

@ -23,16 +23,12 @@ import {
} from '@codemirror/state' } from '@codemirror/state'
import { completionStatus } from '@codemirror/autocomplete' import { completionStatus } from '@codemirror/autocomplete'
import { import {
TransactionAnnotation,
offsetToPos, offsetToPos,
posToOffset, posToOffset,
LanguageServerOptions, LanguageServerOptions,
LanguageServerClient, LanguageServerClient,
docPathFacet, docPathFacet,
languageId, languageId,
TransactionInfo,
updateInfo,
RelevantUpdate,
lspPlugin, lspPlugin,
} from '@kittycad/codemirror-lsp-client' } from '@kittycad/codemirror-lsp-client'
import { deferExecution } from 'lib/utils' import { deferExecution } from 'lib/utils'
@ -193,36 +189,6 @@ const completionDecoration = StateField.define<CompletionState>({
), ),
}) })
export const relevantUpdate = (update: ViewUpdate): RelevantUpdate => {
const infos = updateInfo(update)
// Make sure we are not in a snippet
if (infos.some((info: TransactionInfo) => info.inSnippet)) {
return {
overall: false,
userSelect: false,
time: null,
}
}
return {
overall: infos.some(
(info: TransactionInfo) =>
info.transaction.annotation(copilotPluginEvent.type) !== undefined ||
info.annotations.includes(TransactionAnnotation.UserSelect) ||
info.annotations.includes(TransactionAnnotation.UserInput) ||
info.annotations.includes(TransactionAnnotation.UserDelete) ||
info.annotations.includes(TransactionAnnotation.UserUndo) ||
info.annotations.includes(TransactionAnnotation.UserRedo) ||
info.annotations.includes(TransactionAnnotation.UserMove)
),
userSelect: infos.some((info: TransactionInfo) =>
info.annotations.includes(TransactionAnnotation.UserSelect)
),
time: infos.length ? infos[0].time : null,
}
}
// A view plugin that requests completions from the server after a delay // A view plugin that requests completions from the server after a delay
export class CompletionRequester implements PluginValue { export class CompletionRequester implements PluginValue {
private client: LanguageServerClient private client: LanguageServerClient
@ -243,19 +209,39 @@ export class CompletionRequester implements PluginValue {
} }
update(viewUpdate: ViewUpdate) { update(viewUpdate: ViewUpdate) {
const isRelevant = relevantUpdate(viewUpdate) // Make sure we are in a state where we can request completions.
if (!isRelevant.overall) { if (!editorManager.copilotEnabled) {
return return
} }
let isUserSelect = false
let isRelevant = false
for (const tr of viewUpdate.transactions) {
if (tr.isUserEvent('select')) {
isUserSelect = true
break
} else if (tr.isUserEvent('input')) {
isRelevant = true
} else if (tr.isUserEvent('delete')) {
isRelevant = true
} else if (tr.isUserEvent('undo')) {
isRelevant = true
} else if (tr.isUserEvent('redo')) {
isRelevant = true
} else if (tr.isUserEvent('move')) {
isRelevant = true
} else if (tr.annotation(copilotPluginEvent.type) !== undefined) {
isRelevant = true
}
}
// If we have a user select event, we want to clear the ghost text. // If we have a user select event, we want to clear the ghost text.
if (isRelevant.userSelect) { if (isUserSelect) {
this._deffererUserSelect(true) this._deffererUserSelect(true)
return return
} }
// Make sure we are in a state where we can request completions. if (!isRelevant) {
if (!editorManager.copilotEnabled) {
return return
} }

View File

@ -2,12 +2,9 @@ import { Extension } from '@codemirror/state'
import { ViewPlugin, PluginValue, ViewUpdate } from '@codemirror/view' import { ViewPlugin, PluginValue, ViewUpdate } from '@codemirror/view'
import { import {
LanguageServerOptions, LanguageServerOptions,
updateInfo,
TransactionInfo,
RelevantUpdate,
TransactionAnnotation,
LanguageServerClient, LanguageServerClient,
lspPlugin, lspPlugin,
lspFormatCodeEvent,
} from '@kittycad/codemirror-lsp-client' } from '@kittycad/codemirror-lsp-client'
import { deferExecution } from 'lib/utils' import { deferExecution } from 'lib/utils'
import { codeManager, editorManager, kclManager } from 'lib/singletons' import { codeManager, editorManager, kclManager } from 'lib/singletons'
@ -18,34 +15,6 @@ import { UpdateCanExecuteResponse } from 'wasm-lib/kcl/bindings/UpdateCanExecute
const changesDelay = 600 const changesDelay = 600
export const relevantUpdate = (update: ViewUpdate): RelevantUpdate => {
const infos = updateInfo(update)
// Make sure we are not in a snippet
if (infos.some((info: TransactionInfo) => info.inSnippet)) {
return {
overall: false,
userSelect: false,
time: null,
}
}
return {
overall: infos.some(
(info: TransactionInfo) =>
info.annotations.includes(TransactionAnnotation.UserSelect) ||
info.annotations.includes(TransactionAnnotation.UserInput) ||
info.annotations.includes(TransactionAnnotation.UserDelete) ||
info.annotations.includes(TransactionAnnotation.UserUndo) ||
info.annotations.includes(TransactionAnnotation.UserRedo) ||
info.annotations.includes(TransactionAnnotation.UserMove) ||
info.annotations.includes(TransactionAnnotation.FormatCode)
),
userSelect: infos.some((info: TransactionInfo) =>
info.annotations.includes(TransactionAnnotation.UserSelect)
),
time: infos.length ? infos[0].time : null,
}
}
// A view plugin that requests completions from the server after a delay // A view plugin that requests completions from the server after a delay
export class KclPlugin implements PluginValue { export class KclPlugin implements PluginValue {
private viewUpdate: ViewUpdate | null = null private viewUpdate: ViewUpdate | null = null
@ -75,18 +44,38 @@ export class KclPlugin implements PluginValue {
this.viewUpdate = viewUpdate this.viewUpdate = viewUpdate
editorManager.setEditorView(viewUpdate.view) editorManager.setEditorView(viewUpdate.view)
const isRelevant = relevantUpdate(viewUpdate) let isUserSelect = false
if (!isRelevant.overall) { let isRelevant = false
return for (const tr of viewUpdate.transactions) {
if (tr.isUserEvent('select')) {
isUserSelect = true
break
} else if (tr.isUserEvent('input')) {
isRelevant = true
} else if (tr.isUserEvent('delete')) {
isRelevant = true
} else if (tr.isUserEvent('undo')) {
isRelevant = true
} else if (tr.isUserEvent('redo')) {
isRelevant = true
} else if (tr.isUserEvent('move')) {
isRelevant = true
} else if (tr.annotation(lspFormatCodeEvent.type)) {
isRelevant = true
}
} }
// If we have a user select event, we want to update what parts are // If we have a user select event, we want to update what parts are
// highlighted. // highlighted.
if (isRelevant.userSelect) { if (isUserSelect) {
this._deffererUserSelect(true) this._deffererUserSelect(true)
return return
} }
if (!isRelevant) {
return
}
if (!viewUpdate.docChanged) { if (!viewUpdate.docChanged) {
return return
} }