Codemirror lsp enhance (#6580)
* codemirror side Signed-off-by: Jess Frazelle <github@jessfraz.com> * codemirror actions Signed-off-by: Jess Frazelle <github@jessfraz.com> * codemirror actions Signed-off-by: Jess Frazelle <github@jessfraz.com> * code mirror now shows lint suggestions Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix hanging params with test Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates for signature help Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix clone Signed-off-by: Jess Frazelle <github@jessfraz.com> * add tests Signed-off-by: Jess Frazelle <github@jessfraz.com> * add tests Signed-off-by: Jess Frazelle <github@jessfraz.com> * clippy Signed-off-by: Jess Frazelle <github@jessfraz.com> * clippy Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * Update packages/codemirror-lsp-client/src/plugin/lsp.ts Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com> * z-index Signed-off-by: Jess Frazelle <github@jessfraz.com> * playwright tests Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com> Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
This commit is contained in:
@ -196,6 +196,68 @@ sketch001 = startSketchOn(XY)
|
||||
)
|
||||
})
|
||||
|
||||
test('F2 can rename a variable', async ({ page, homePage, scene }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`myVARName = 100
|
||||
|
||||
a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [myVARName, 0])
|
||||
|> yLine(length = -100.0)
|
||||
|> xLine(length = -100.0)
|
||||
|> yLine(length = 100.0)
|
||||
|> close()
|
||||
|> extrude(length = 12)`
|
||||
)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await scene.connectionEstablished()
|
||||
|
||||
// check no error to begin with
|
||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||
|
||||
await u.codeLocator.click()
|
||||
|
||||
// Move the cursor to the start of the code
|
||||
await page.keyboard.press('ControlOrMeta+Home')
|
||||
await page.keyboard.press('ArrowRight')
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
// Press F2 to rename the variable
|
||||
await page.keyboard.press('F2')
|
||||
|
||||
// Wait for the rename box.
|
||||
await expect(page.locator('.cm-rename-popup')).toBeVisible()
|
||||
|
||||
// Make sure we are focused on the rename box
|
||||
await expect(page.locator('.cm-rename-popup input')).toBeFocused()
|
||||
|
||||
// Type the new name
|
||||
await page.keyboard.type('myNewName')
|
||||
// Press Enter to accept the rename
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Ensure we have the new name
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`myNewName = 100
|
||||
|
||||
a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [myNewName, 0])
|
||||
|> yLine(length = -100.0)
|
||||
|> xLine(length = -100.0)
|
||||
|> yLine(length = 100.0)
|
||||
|> close()
|
||||
|> extrude(length = 12)`.replaceAll('\n', '')
|
||||
)
|
||||
})
|
||||
|
||||
test('if you click the format button it formats your code and executes so lints are still there', async ({
|
||||
page,
|
||||
homePage,
|
||||
@ -481,6 +543,113 @@ sketch_001 = startSketchOn(XY)
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('you can accept the suggestion from a lint', async ({
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
}) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`a1 = startSketchOn({
|
||||
origin = { x = 0, y = 0, z = 0 },
|
||||
xAxis = { x = 1, y = 0, z = 0 },
|
||||
yAxis = { x = 0, y = 12, z = 0 },
|
||||
})
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [100.0, 0])
|
||||
|> yLine(length = -100.0)
|
||||
|> xLine(length = -100.0)
|
||||
|> yLine(length = 100.0)
|
||||
|> close()
|
||||
|> extrude(length = 3.14)`
|
||||
)
|
||||
})
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await scene.connectionEstablished()
|
||||
|
||||
await expect(page.locator('.cm-lint-marker-info')).toBeVisible()
|
||||
|
||||
// error in guter
|
||||
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
||||
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-info')
|
||||
await expect(
|
||||
page.getByText('offsetPlane should be used').first()
|
||||
).toBeVisible()
|
||||
|
||||
// select the line that's causing the error and delete it
|
||||
// accept the change
|
||||
await page.getByText('use offsetPlane instead').click()
|
||||
|
||||
// Ensure we have the new code
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`a1 = startSketchOn(offsetPlane(XY, offset = 12))
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [100.0, 0])
|
||||
|> yLine(length = -100.0)
|
||||
|> xLine(length = -100.0)
|
||||
|> yLine(length = 100.0)
|
||||
|> close()
|
||||
|> extrude(length = 3.14)`.replaceAll('\n', '')
|
||||
)
|
||||
})
|
||||
|
||||
test('signature help triggered by comma', async ({
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
}) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`myVARName = 100
|
||||
|
||||
a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [myVARName, 0])
|
||||
|> yLine(length = -100.0)
|
||||
|> xLine(length = -100.0)
|
||||
|> yLine(length = 100.0)
|
||||
|> close()
|
||||
|> extrude(length = 12`
|
||||
)
|
||||
})
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await scene.connectionEstablished()
|
||||
|
||||
// Expect the signature help to NOT be visible
|
||||
await expect(page.locator('.cm-signature-tooltip')).not.toBeVisible()
|
||||
|
||||
// Click in the editor
|
||||
await page.locator('.cm-content').click()
|
||||
|
||||
// Go to the end of the code
|
||||
await page.keyboard.press('ControlOrMeta+End')
|
||||
// Type a comma
|
||||
await page.keyboard.press(',')
|
||||
|
||||
// Wait for the signature help to show
|
||||
await expect(page.locator('.cm-signature-tooltip')).toBeVisible()
|
||||
|
||||
// Make sure the parameters are correct
|
||||
await expect(page.locator('.cm-signature-tooltip')).toContainText(
|
||||
'sketches:'
|
||||
)
|
||||
|
||||
// Make sure the tooltip goes away after a timeout.
|
||||
await page.waitForTimeout(12000)
|
||||
|
||||
await expect(page.locator('.cm-signature-tooltip')).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('if you write kcl with lint errors you get lints', async ({
|
||||
page,
|
||||
homePage,
|
||||
|
@ -14,6 +14,7 @@ interface LSPRequestMap {
|
||||
LSP.CompletionParams,
|
||||
LSP.CompletionItem[] | LSP.CompletionList | null,
|
||||
]
|
||||
'completionItem/resolve': [LSP.CompletionItem, LSP.CompletionItem]
|
||||
'textDocument/semanticTokens/full': [
|
||||
LSP.SemanticTokensParams,
|
||||
LSP.SemanticTokens,
|
||||
@ -23,6 +24,23 @@ interface LSPRequestMap {
|
||||
LSP.TextEdit[] | null,
|
||||
]
|
||||
'textDocument/foldingRange': [LSP.FoldingRangeParams, LSP.FoldingRange[]]
|
||||
'textDocument/signatureHelp': [
|
||||
LSP.SignatureHelpParams,
|
||||
LSP.SignatureHelp | null,
|
||||
]
|
||||
'textDocument/codeAction': [
|
||||
LSP.CodeActionParams,
|
||||
(LSP.Command | LSP.CodeAction)[] | null,
|
||||
]
|
||||
'textDocument/rename': [LSP.RenameParams, LSP.WorkspaceEdit | null]
|
||||
'textDocument/prepareRename': [
|
||||
LSP.PrepareRenameParams,
|
||||
LSP.Range | LSP.PrepareRenameResult | null,
|
||||
]
|
||||
'textDocument/definition': [
|
||||
LSP.DefinitionParams,
|
||||
LSP.Definition | LSP.DefinitionLink[] | null,
|
||||
]
|
||||
}
|
||||
|
||||
// Client to server
|
||||
@ -124,7 +142,7 @@ export class LanguageServerClient {
|
||||
async textDocumentSemanticTokensFull(params: LSP.SemanticTokensParams) {
|
||||
const serverCapabilities = this.getServerCapabilities()
|
||||
if (!serverCapabilities.semanticTokensProvider) {
|
||||
return
|
||||
return null
|
||||
}
|
||||
|
||||
return this.request('textDocument/semanticTokens/full', params)
|
||||
@ -133,7 +151,7 @@ export class LanguageServerClient {
|
||||
async textDocumentHover(params: LSP.HoverParams) {
|
||||
const serverCapabilities = this.getServerCapabilities()
|
||||
if (!serverCapabilities.hoverProvider) {
|
||||
return
|
||||
return null
|
||||
}
|
||||
return await this.request('textDocument/hover', params)
|
||||
}
|
||||
@ -141,7 +159,7 @@ export class LanguageServerClient {
|
||||
async textDocumentFormatting(params: LSP.DocumentFormattingParams) {
|
||||
const serverCapabilities = this.getServerCapabilities()
|
||||
if (!serverCapabilities.documentFormattingProvider) {
|
||||
return
|
||||
return null
|
||||
}
|
||||
return await this.request('textDocument/formatting', params)
|
||||
}
|
||||
@ -149,7 +167,7 @@ export class LanguageServerClient {
|
||||
async textDocumentFoldingRange(params: LSP.FoldingRangeParams) {
|
||||
const serverCapabilities = this.getServerCapabilities()
|
||||
if (!serverCapabilities.foldingRangeProvider) {
|
||||
return
|
||||
return null
|
||||
}
|
||||
return await this.request('textDocument/foldingRange', params)
|
||||
}
|
||||
@ -157,10 +175,58 @@ export class LanguageServerClient {
|
||||
async textDocumentCompletion(params: LSP.CompletionParams) {
|
||||
const serverCapabilities = this.getServerCapabilities()
|
||||
if (!serverCapabilities.completionProvider) {
|
||||
return
|
||||
return null
|
||||
}
|
||||
const response = await this.request('textDocument/completion', params)
|
||||
return response
|
||||
return await this.request('textDocument/completion', params)
|
||||
}
|
||||
|
||||
async completionItemResolve(params: LSP.CompletionItem) {
|
||||
const serverCapabilities = this.getServerCapabilities()
|
||||
if (!serverCapabilities.completionProvider) {
|
||||
return null
|
||||
}
|
||||
return await this.request('completionItem/resolve', params)
|
||||
}
|
||||
|
||||
async textDocumentSignatureHelp(params: LSP.SignatureHelpParams) {
|
||||
const serverCapabilities = this.getServerCapabilities()
|
||||
if (!serverCapabilities.signatureHelpProvider) {
|
||||
return null
|
||||
}
|
||||
return await this.request('textDocument/signatureHelp', params)
|
||||
}
|
||||
|
||||
async textDocumentCodeAction(params: LSP.CodeActionParams) {
|
||||
const serverCapabilities = this.getServerCapabilities()
|
||||
if (!serverCapabilities.codeActionProvider) {
|
||||
return null
|
||||
}
|
||||
return await this.request('textDocument/codeAction', params)
|
||||
}
|
||||
|
||||
async textDocumentRename(params: LSP.RenameParams) {
|
||||
const serverCapabilities = this.getServerCapabilities()
|
||||
if (!serverCapabilities.renameProvider) {
|
||||
return null
|
||||
}
|
||||
|
||||
return await this.request('textDocument/rename', params)
|
||||
}
|
||||
|
||||
async textDocumentPrepareRename(params: LSP.PrepareRenameParams) {
|
||||
const serverCapabilities = this.getServerCapabilities()
|
||||
if (!serverCapabilities.renameProvider) {
|
||||
return null
|
||||
}
|
||||
return await this.request('textDocument/prepareRename', params)
|
||||
}
|
||||
|
||||
async textDocumentDefinition(params: LSP.DefinitionParams) {
|
||||
const serverCapabilities = this.getServerCapabilities()
|
||||
if (!serverCapabilities.definitionProvider) {
|
||||
return null
|
||||
}
|
||||
return await this.request('textDocument/definition', params)
|
||||
}
|
||||
|
||||
attachPlugin(plugin: LanguageServerPlugin) {
|
||||
|
@ -18,7 +18,9 @@ export { Codec } from './client/codec/utils'
|
||||
export {
|
||||
lspDiagnosticsEvent,
|
||||
lspFormatCodeEvent,
|
||||
lspRenameEvent,
|
||||
lspSemanticTokensEvent,
|
||||
lspCodeActionEvent,
|
||||
} from './plugin/annotation'
|
||||
export {
|
||||
LanguageServerPlugin,
|
||||
|
@ -4,9 +4,13 @@ export enum LspAnnotation {
|
||||
SemanticTokens = 'semantic-tokens',
|
||||
FormatCode = 'format-code',
|
||||
Diagnostics = 'diagnostics',
|
||||
Rename = 'rename',
|
||||
CodeAction = 'code-action',
|
||||
}
|
||||
|
||||
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 const lspRenameEvent = lspEvent.of(LspAnnotation.Rename)
|
||||
export const lspCodeActionEvent = lspEvent.of(LspAnnotation.CodeAction)
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
prevSnippetField,
|
||||
startCompletion,
|
||||
} from '@codemirror/autocomplete'
|
||||
import type { CompletionContext } from '@codemirror/autocomplete'
|
||||
import { syntaxTree } from '@codemirror/language'
|
||||
import type { Extension } from '@codemirror/state'
|
||||
import { Prec } from '@codemirror/state'
|
||||
@ -66,7 +67,7 @@ export default function lspAutocompleteExt(
|
||||
defaultKeymap: false,
|
||||
override: [
|
||||
async (context) => {
|
||||
const { state, pos, explicit, view } = context
|
||||
const { state, pos, view } = context
|
||||
let value = view?.plugin(plugin)
|
||||
if (!value) return null
|
||||
|
||||
@ -77,37 +78,55 @@ export default function lspAutocompleteExt(
|
||||
)
|
||||
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]
|
||||
const cmpTriggers = getCompletionTriggerKind(
|
||||
context,
|
||||
value.client.getServerCapabilities().completionProvider
|
||||
?.triggerCharacters ?? []
|
||||
)
|
||||
) {
|
||||
trigKind = CompletionTriggerKind.TriggerCharacter
|
||||
trigChar = line.text[pos - line.from - 1]
|
||||
}
|
||||
if (
|
||||
trigKind === CompletionTriggerKind.Invoked &&
|
||||
!context.matchBefore(/\w+$/)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
if (!cmpTriggers) return null
|
||||
|
||||
return await value.requestCompletion(
|
||||
context,
|
||||
offsetToPos(state.doc, pos),
|
||||
{
|
||||
triggerKind: trigKind,
|
||||
triggerCharacter: trigChar,
|
||||
}
|
||||
cmpTriggers
|
||||
)
|
||||
},
|
||||
],
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
export function getCompletionTriggerKind(
|
||||
context: CompletionContext,
|
||||
triggerCharacters: string[],
|
||||
matchBeforePattern?: RegExp
|
||||
): {
|
||||
triggerKind: CompletionTriggerKind
|
||||
triggerCharacter?: string
|
||||
} | null {
|
||||
const { state, pos, explicit } = context
|
||||
const line = state.doc.lineAt(pos)
|
||||
|
||||
// Determine trigger kind and character
|
||||
let triggerKind: CompletionTriggerKind = CompletionTriggerKind.Invoked
|
||||
let triggerCharacter: string | undefined
|
||||
|
||||
// Check if completion was triggered by a special character
|
||||
const prevChar = line.text[pos - line.from - 1] || ''
|
||||
const isTriggerChar = triggerCharacters?.includes(prevChar)
|
||||
|
||||
if (!explicit && isTriggerChar) {
|
||||
triggerKind = CompletionTriggerKind.TriggerCharacter
|
||||
triggerCharacter = prevChar
|
||||
}
|
||||
// For manual invocation, only show completions when typing
|
||||
// Use the provided pattern or default to words, dots, commas, or slashes
|
||||
if (
|
||||
triggerKind === CompletionTriggerKind.Invoked &&
|
||||
!context.matchBefore(matchBeforePattern || /(\w+|\w+\.|\/|,)$/)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
return { triggerKind, triggerCharacter }
|
||||
}
|
||||
|
@ -0,0 +1,40 @@
|
||||
import type { Extension } from '@codemirror/state'
|
||||
import { Prec } from '@codemirror/state'
|
||||
import type { ViewPlugin } from '@codemirror/view'
|
||||
import { keymap } from '@codemirror/view'
|
||||
|
||||
import type { LanguageServerPlugin } from './lsp'
|
||||
import { offsetToPos, showErrorMessage } from './util'
|
||||
|
||||
export default function lspGoToDefinitionExt(
|
||||
plugin: ViewPlugin<LanguageServerPlugin>
|
||||
): Extension {
|
||||
return [
|
||||
Prec.highest(
|
||||
keymap.of([
|
||||
{
|
||||
key: 'F12',
|
||||
run: (view) => {
|
||||
if (!plugin) {
|
||||
return false
|
||||
}
|
||||
|
||||
const value = view.plugin(plugin)
|
||||
if (!value) return false
|
||||
|
||||
const pos = view.state.selection.main.head
|
||||
value
|
||||
.requestDefinition(view, offsetToPos(view.state.doc, pos))
|
||||
.catch((error) =>
|
||||
showErrorMessage(
|
||||
view,
|
||||
`Go to definition failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
)
|
||||
)
|
||||
return true
|
||||
},
|
||||
},
|
||||
])
|
||||
),
|
||||
]
|
||||
}
|
@ -4,6 +4,7 @@ import type {
|
||||
CompletionResult,
|
||||
} from '@codemirror/autocomplete'
|
||||
import { completeFromList, snippetCompletion } from '@codemirror/autocomplete'
|
||||
import type { Action, Diagnostic } from '@codemirror/lint'
|
||||
import { linter } from '@codemirror/lint'
|
||||
import type { Extension, StateEffect } from '@codemirror/state'
|
||||
import { Facet, Transaction } from '@codemirror/state'
|
||||
@ -24,14 +25,29 @@ import { DiagnosticSeverity } from 'vscode-languageserver-protocol'
|
||||
import { URI } from 'vscode-uri'
|
||||
|
||||
import type { LanguageServerClient } from '../client'
|
||||
import { lspFormatCodeEvent, lspSemanticTokensEvent } from './annotation'
|
||||
import {
|
||||
lspFormatCodeEvent,
|
||||
lspSemanticTokensEvent,
|
||||
lspRenameEvent,
|
||||
lspCodeActionEvent,
|
||||
} from './annotation'
|
||||
import lspAutocompleteExt, { CompletionItemKindMap } from './autocomplete'
|
||||
import lspFormatExt from './format'
|
||||
import lspHoverExt from './hover'
|
||||
import lspIndentExt from './indent'
|
||||
import type { SemanticToken } from './semantic-tokens'
|
||||
import lspSemanticTokensExt, { addToken } from './semantic-tokens'
|
||||
import { formatMarkdownContents, posToOffset } from './util'
|
||||
import {
|
||||
formatContents,
|
||||
offsetToPos,
|
||||
posToOffset,
|
||||
posToOffsetOrZero,
|
||||
showErrorMessage,
|
||||
} from './util'
|
||||
import { isArray } from '../lib/utils'
|
||||
import lspGoToDefinitionExt from './go-to-definition'
|
||||
import lspRenameExt from './rename'
|
||||
import lspSignatureHelpExt from './signature-help'
|
||||
|
||||
const useLast = (values: readonly any[]) => values.reduce((_, v) => v, '')
|
||||
export const docPathFacet = Facet.define<string, string>({
|
||||
@ -43,6 +59,25 @@ export const workspaceFolders = Facet.define<
|
||||
LSP.WorkspaceFolder[]
|
||||
>({ combine: useLast })
|
||||
|
||||
const severityMap: Record<DiagnosticSeverity, Diagnostic['severity']> = {
|
||||
[DiagnosticSeverity.Error]: 'error',
|
||||
[DiagnosticSeverity.Warning]: 'warning',
|
||||
[DiagnosticSeverity.Information]: 'info',
|
||||
[DiagnosticSeverity.Hint]: 'info',
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of a definition lookup operation
|
||||
*/
|
||||
export interface DefinitionResult {
|
||||
/** URI of the target document containing the definition */
|
||||
uri: string
|
||||
/** Range in the document where the definition is located */
|
||||
range: LSP.Range
|
||||
/** Whether the definition is in a different file than the current document */
|
||||
isExternalDocument: boolean
|
||||
}
|
||||
|
||||
export interface LanguageServerOptions {
|
||||
// We assume this is the main project directory, we are currently working in.
|
||||
workspaceFolders: LSP.WorkspaceFolder[]
|
||||
@ -58,6 +93,9 @@ export interface LanguageServerOptions {
|
||||
|
||||
doSemanticTokens?: boolean
|
||||
doFoldingRanges?: boolean
|
||||
|
||||
/** Callback triggered when a go-to-definition action is performed */
|
||||
onGoToDefinition?: (result: DefinitionResult) => void
|
||||
}
|
||||
|
||||
export class LanguageServerPlugin implements PluginValue {
|
||||
@ -82,6 +120,8 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
// document.
|
||||
private sendScheduled: number | null = null
|
||||
|
||||
private onGoToDefinition: ((result: DefinitionResult) => void) | undefined
|
||||
|
||||
constructor(
|
||||
options: LanguageServerOptions,
|
||||
private view: EditorView
|
||||
@ -104,6 +144,8 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
|
||||
this.processLspNotification = options.processLspNotification
|
||||
|
||||
this.onGoToDefinition = options.onGoToDefinition
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.initialize({
|
||||
documentText: this.getDocText(),
|
||||
@ -185,8 +227,8 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
dom.classList.add('documentation')
|
||||
dom.classList.add('hover-tooltip')
|
||||
dom.style.zIndex = '99999999'
|
||||
if (this.allowHTMLContent) dom.innerHTML = formatMarkdownContents(contents)
|
||||
else dom.textContent = formatMarkdownContents(contents)
|
||||
if (this.allowHTMLContent) dom.innerHTML = formatContents(contents)
|
||||
else dom.textContent = formatContents(contents)
|
||||
return { pos, end, create: (view) => ({ dom }), above: true }
|
||||
}
|
||||
|
||||
@ -317,7 +359,7 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
triggerCharacter,
|
||||
}: {
|
||||
triggerKind: CompletionTriggerKind
|
||||
triggerCharacter: string | undefined
|
||||
triggerCharacter?: string
|
||||
}
|
||||
): Promise<CompletionResult | null> {
|
||||
if (
|
||||
@ -379,8 +421,7 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
const deprecatedHtml = deprecated
|
||||
? '<p><strong>Deprecated</strong></p>'
|
||||
: ''
|
||||
const htmlString =
|
||||
deprecatedHtml + formatMarkdownContents(documentation)
|
||||
const htmlString = deprecatedHtml + formatContents(documentation)
|
||||
const htmlNode = document.createElement('div')
|
||||
htmlNode.style.display = 'contents'
|
||||
htmlNode.innerHTML = htmlString
|
||||
@ -406,6 +447,660 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
return completeFromList(options)(context)
|
||||
}
|
||||
|
||||
async requestDefinition(
|
||||
view: EditorView,
|
||||
{ line, character }: { line: number; character: number }
|
||||
) {
|
||||
if (
|
||||
!(
|
||||
this.client.ready &&
|
||||
this.client.getServerCapabilities().definitionProvider
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
const result = await this.client.textDocumentDefinition({
|
||||
textDocument: { uri: this.getDocUri() },
|
||||
position: { line, character },
|
||||
})
|
||||
|
||||
if (!result) return
|
||||
|
||||
const locations = isArray(result) ? result : [result]
|
||||
if (locations.length === 0) return
|
||||
|
||||
// For now just handle the first location
|
||||
const location = locations[0]
|
||||
if (!location) return
|
||||
const uri = 'uri' in location ? location.uri : location.targetUri
|
||||
const range = 'range' in location ? location.range : location.targetRange
|
||||
|
||||
// Check if the definition is in a different document
|
||||
const isExternalDocument = uri !== this.getDocUri()
|
||||
|
||||
// Create the definition result
|
||||
const definitionResult: DefinitionResult = {
|
||||
uri,
|
||||
range,
|
||||
isExternalDocument,
|
||||
}
|
||||
|
||||
// If it's the same document, update the selection
|
||||
if (!isExternalDocument) {
|
||||
view.dispatch(
|
||||
view.state.update({
|
||||
selection: {
|
||||
anchor: posToOffsetOrZero(view.state.doc, range.start),
|
||||
head: posToOffset(view.state.doc, range.end),
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (this.onGoToDefinition) {
|
||||
this.onGoToDefinition(definitionResult)
|
||||
}
|
||||
|
||||
return definitionResult
|
||||
}
|
||||
|
||||
async requestCodeActions(
|
||||
range: LSP.Range,
|
||||
diagnosticCodes: string[]
|
||||
): Promise<(LSP.Command | LSP.CodeAction)[] | null> {
|
||||
if (
|
||||
!(
|
||||
this.client.ready &&
|
||||
this.client.getServerCapabilities().codeActionProvider
|
||||
)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
return await this.client.textDocumentCodeAction({
|
||||
textDocument: { uri: this.getDocUri() },
|
||||
range,
|
||||
context: {
|
||||
diagnostics: [
|
||||
{
|
||||
range,
|
||||
code: diagnosticCodes[0],
|
||||
source: this.getLanguageId(),
|
||||
message: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async requestRename(
|
||||
view: EditorView,
|
||||
{ line, character }: { line: number; character: number }
|
||||
) {
|
||||
if (
|
||||
!(this.client.getServerCapabilities().renameProvider && this.client.ready)
|
||||
) {
|
||||
showErrorMessage(view, 'Rename not supported by language server')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// First check if rename is possible at this position
|
||||
const prepareResult = await this.client
|
||||
.textDocumentPrepareRename({
|
||||
textDocument: { uri: this.getDocUri() },
|
||||
position: { line, character },
|
||||
})
|
||||
.catch(() => {
|
||||
// In case prepareRename is not supported,
|
||||
// we fallback to the default implementation
|
||||
return this.prepareRenameFallback(view, {
|
||||
line,
|
||||
character,
|
||||
})
|
||||
})
|
||||
|
||||
if (!prepareResult || 'defaultBehavior' in prepareResult) {
|
||||
showErrorMessage(view, 'Cannot rename this symbol')
|
||||
return
|
||||
}
|
||||
|
||||
// Create popup input
|
||||
const popup = document.createElement('div')
|
||||
popup.className = 'cm-rename-popup'
|
||||
popup.style.cssText =
|
||||
'position: absolute; padding: 4px; background: white; border: 1px solid #ddd; box-shadow: 0 2px 8px rgba(0,0,0,.15); z-index: 99;'
|
||||
|
||||
const input = document.createElement('input')
|
||||
input.type = 'text'
|
||||
input.style.cssText =
|
||||
'width: 200px; padding: 4px; border: 1px solid #ddd;'
|
||||
|
||||
// Get current word as default value
|
||||
const range =
|
||||
'range' in prepareResult ? prepareResult.range : prepareResult
|
||||
const from = posToOffset(view.state.doc, range.start)
|
||||
if (from == null) {
|
||||
return
|
||||
}
|
||||
const to = posToOffset(view.state.doc, range.end)
|
||||
input.value = view.state.doc.sliceString(from, to)
|
||||
|
||||
popup.appendChild(input)
|
||||
|
||||
// Position the popup near the word
|
||||
const coords = view.coordsAtPos(from)
|
||||
if (!coords) return
|
||||
|
||||
popup.style.left = `${coords.left}px`
|
||||
popup.style.top = `${coords.bottom + 5}px`
|
||||
|
||||
// Handle input
|
||||
const handleRename = async () => {
|
||||
const newName = input.value.trim()
|
||||
if (!newName) {
|
||||
showErrorMessage(view, 'New name cannot be empty')
|
||||
popup.remove()
|
||||
return
|
||||
}
|
||||
|
||||
if (newName === input.defaultValue) {
|
||||
popup.remove()
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const edit = await this.client.textDocumentRename({
|
||||
textDocument: { uri: this.getDocUri() },
|
||||
position: { line, character },
|
||||
newName,
|
||||
})
|
||||
|
||||
await this.applyRenameEdit(view, edit)
|
||||
} catch (error) {
|
||||
showErrorMessage(
|
||||
view,
|
||||
`Rename failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
)
|
||||
} finally {
|
||||
popup.remove()
|
||||
}
|
||||
}
|
||||
|
||||
input.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
handleRename()
|
||||
} else if (e.key === 'Escape') {
|
||||
popup.remove()
|
||||
}
|
||||
e.stopPropagation() // Prevent editor handling
|
||||
})
|
||||
|
||||
// Handle clicks outside
|
||||
const handleOutsideClick = (e: MouseEvent) => {
|
||||
if (!popup.contains(e.target as Node)) {
|
||||
popup.remove()
|
||||
document.removeEventListener('mousedown', handleOutsideClick)
|
||||
}
|
||||
}
|
||||
document.addEventListener('mousedown', handleOutsideClick)
|
||||
|
||||
// Add to DOM
|
||||
document.body.appendChild(popup)
|
||||
input.focus()
|
||||
input.select()
|
||||
} catch (error) {
|
||||
showErrorMessage(
|
||||
view,
|
||||
`Rename failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request signature help from the language server
|
||||
* @param view The editor view
|
||||
* @param position The cursor position
|
||||
* @returns A tooltip with the signature help information or null if not available
|
||||
*/
|
||||
public async requestSignatureHelp(
|
||||
view: EditorView,
|
||||
{
|
||||
line,
|
||||
character,
|
||||
}: {
|
||||
line: number
|
||||
character: number
|
||||
},
|
||||
triggerCharacter: string | undefined = undefined
|
||||
): Promise<Tooltip | null> {
|
||||
// Check if signature help is enabled
|
||||
if (
|
||||
!(
|
||||
this.client.ready &&
|
||||
this.client.getServerCapabilities().signatureHelpProvider
|
||||
)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
// Request signature help
|
||||
const result = await this.client.textDocumentSignatureHelp({
|
||||
textDocument: { uri: this.getDocUri() },
|
||||
position: { line, character },
|
||||
context: {
|
||||
isRetrigger: false,
|
||||
triggerKind: 1, // Invoked
|
||||
triggerCharacter,
|
||||
},
|
||||
})
|
||||
|
||||
if (!result?.signatures || result.signatures.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Create the tooltip container
|
||||
const dom = this.createTooltipContainer()
|
||||
|
||||
// Get active signature
|
||||
const activeSignatureIndex = result.activeSignature ?? 0
|
||||
const activeSignature =
|
||||
result.signatures[activeSignatureIndex] || result.signatures[0]
|
||||
|
||||
if (!activeSignature) {
|
||||
return null
|
||||
}
|
||||
|
||||
const activeParameterIndex =
|
||||
result.activeParameter || activeSignature.activeParameter
|
||||
|
||||
// Create and add signature display element
|
||||
const signatureElement = this.createSignatureElement(
|
||||
activeSignature,
|
||||
activeParameterIndex
|
||||
)
|
||||
dom.appendChild(signatureElement)
|
||||
|
||||
// Add documentation if available
|
||||
if (activeSignature.documentation) {
|
||||
dom.appendChild(
|
||||
this.createDocumentationElement(activeSignature.documentation)
|
||||
)
|
||||
}
|
||||
|
||||
if (activeParameterIndex) {
|
||||
// Add parameter documentation if available
|
||||
const activeParam = activeSignature.parameters?.[activeParameterIndex]
|
||||
|
||||
if (activeParam?.documentation) {
|
||||
dom.appendChild(this.createParameterDocElement(activeParam))
|
||||
}
|
||||
} else {
|
||||
// Append docs for all the parameters.
|
||||
activeSignature.parameters?.forEach((param) => {
|
||||
if (param.documentation) {
|
||||
dom.appendChild(this.createParameterDocElement(param))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Position tooltip at cursor
|
||||
let pos = posToOffset(view.state.doc, { line, character })
|
||||
if (pos === null || pos === undefined) return null
|
||||
|
||||
return {
|
||||
pos,
|
||||
end: pos,
|
||||
create: (_view) => ({ dom }),
|
||||
above: true,
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Signature help error:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a signature help tooltip at the specified position
|
||||
*/
|
||||
public async showSignatureHelpTooltip(
|
||||
view: EditorView,
|
||||
pos: number,
|
||||
triggerCharacter?: string
|
||||
) {
|
||||
const tooltip = await this.requestSignatureHelp(
|
||||
view,
|
||||
offsetToPos(view.state.doc, pos),
|
||||
triggerCharacter
|
||||
)
|
||||
|
||||
if (tooltip) {
|
||||
// Create and show the tooltip manually
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the main tooltip container for signature help
|
||||
*/
|
||||
private createTooltipContainer(): HTMLElement {
|
||||
const dom = document.createElement('div')
|
||||
dom.classList.add('cm-signature-help')
|
||||
return dom
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the signature element with parameter highlighting
|
||||
*/
|
||||
private createSignatureElement(
|
||||
signature: LSP.SignatureInformation,
|
||||
activeParameterIndex?: number
|
||||
): HTMLElement {
|
||||
const signatureElement = document.createElement('div')
|
||||
signatureElement.classList.add('cm-signature')
|
||||
signatureElement.style.cssText =
|
||||
'font-family: monospace; margin-bottom: 4px;'
|
||||
|
||||
if (!signature.label || typeof signature.label !== 'string') {
|
||||
signatureElement.textContent = 'Signature information unavailable'
|
||||
return signatureElement
|
||||
}
|
||||
|
||||
const signatureText = signature.label
|
||||
const parameters = signature.parameters || []
|
||||
|
||||
// If there are no parameters or no active parameter, just show the signature text
|
||||
if (
|
||||
parameters.length === 0 ||
|
||||
!activeParameterIndex ||
|
||||
!parameters[activeParameterIndex]
|
||||
) {
|
||||
signatureElement.textContent = signatureText
|
||||
return signatureElement
|
||||
}
|
||||
|
||||
// Handle parameter highlighting based on the parameter label type
|
||||
const paramLabel = parameters[activeParameterIndex].label
|
||||
|
||||
if (typeof paramLabel === 'string') {
|
||||
// Simple string replacement
|
||||
if (this.allowHTMLContent) {
|
||||
signatureElement.innerHTML = signatureText.replace(
|
||||
paramLabel,
|
||||
`<strong class="cm-signature-active-param">${paramLabel}</strong>`
|
||||
)
|
||||
} else {
|
||||
signatureElement.textContent = signatureText.replace(
|
||||
paramLabel,
|
||||
`«${paramLabel}»`
|
||||
)
|
||||
}
|
||||
} else if (isArray(paramLabel) && paramLabel.length === 2) {
|
||||
// Handle array format [startIndex, endIndex]
|
||||
this.applyRangeHighlighting(
|
||||
signatureElement,
|
||||
signatureText,
|
||||
paramLabel[0],
|
||||
paramLabel[1]
|
||||
)
|
||||
} else {
|
||||
signatureElement.textContent = signatureText
|
||||
}
|
||||
|
||||
return signatureElement
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies parameter highlighting using a range approach
|
||||
*/
|
||||
private applyRangeHighlighting(
|
||||
element: HTMLElement,
|
||||
text: string,
|
||||
startIndex: number,
|
||||
endIndex: number
|
||||
): void {
|
||||
// Clear any existing content
|
||||
element.textContent = ''
|
||||
|
||||
// Split the text into three parts: before, parameter, after
|
||||
const beforeParam = text.substring(0, startIndex)
|
||||
const param = text.substring(startIndex, endIndex)
|
||||
const afterParam = text.substring(endIndex)
|
||||
|
||||
// Add the parts to the element
|
||||
element.appendChild(document.createTextNode(beforeParam))
|
||||
|
||||
const paramSpan = document.createElement('span')
|
||||
paramSpan.classList.add('cm-signature-active-param')
|
||||
paramSpan.style.cssText = 'font-weight: bold; text-decoration: underline;'
|
||||
paramSpan.textContent = param
|
||||
element.appendChild(paramSpan)
|
||||
|
||||
element.appendChild(document.createTextNode(afterParam))
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the documentation element for signatures
|
||||
*/
|
||||
private createDocumentationElement(
|
||||
documentation: string | LSP.MarkupContent
|
||||
): HTMLElement {
|
||||
const docsElement = document.createElement('div')
|
||||
docsElement.classList.add('cm-signature-docs')
|
||||
docsElement.style.cssText = 'margin-top: 4px; color: #666;'
|
||||
|
||||
const formattedContent = formatContents(documentation)
|
||||
|
||||
if (this.allowHTMLContent) {
|
||||
docsElement.innerHTML = formattedContent
|
||||
} else {
|
||||
docsElement.textContent = formattedContent
|
||||
}
|
||||
|
||||
return docsElement
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the parameter documentation element
|
||||
*/
|
||||
private createParameterDocElement(
|
||||
parameter: LSP.ParameterInformation
|
||||
): HTMLElement {
|
||||
const paramDocsElement = document.createElement('div')
|
||||
paramDocsElement.classList.add('cm-parameter-docs')
|
||||
paramDocsElement.style.cssText =
|
||||
'margin-top: 4px; font-style: italic; border-top: 1px solid #eee; padding-top: 4px;'
|
||||
|
||||
const formattedContent =
|
||||
`<strong>${parameter.label}:</strong> ` +
|
||||
formatContents(parameter.documentation)
|
||||
|
||||
if (this.allowHTMLContent) {
|
||||
paramDocsElement.innerHTML = formattedContent
|
||||
} else {
|
||||
paramDocsElement.textContent = formattedContent
|
||||
}
|
||||
|
||||
return paramDocsElement
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback implementation of prepareRename.
|
||||
* We try to find the word at the cursor position and return the range of the word.
|
||||
*/
|
||||
private prepareRenameFallback(
|
||||
view: EditorView,
|
||||
{ line, character }: { line: number; character: number }
|
||||
): LSP.PrepareRenameResult | null {
|
||||
const doc = view.state.doc
|
||||
const lineText = doc.line(line + 1).text
|
||||
const wordRegex = /\w+/g
|
||||
let match: RegExpExecArray | null
|
||||
let start = character
|
||||
let end = character
|
||||
// Find all word matches in the line
|
||||
// biome-ignore lint/suspicious/noAssignInExpressions: <explanation>
|
||||
while ((match = wordRegex.exec(lineText)) !== null) {
|
||||
const matchStart = match.index
|
||||
const matchEnd = match.index + match[0].length
|
||||
|
||||
// Check if cursor position is within or at the boundaries of this word
|
||||
if (character >= matchStart && character <= matchEnd) {
|
||||
start = matchStart
|
||||
end = matchEnd
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (start === character && end === character) {
|
||||
return null // No word found at cursor position
|
||||
}
|
||||
|
||||
return {
|
||||
range: {
|
||||
start: {
|
||||
line,
|
||||
character: start,
|
||||
},
|
||||
end: {
|
||||
line,
|
||||
character: end,
|
||||
},
|
||||
},
|
||||
placeholder: lineText.slice(start, end),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply workspace edit from rename operation
|
||||
* @param view The editor view
|
||||
* @param edit The workspace edit to apply
|
||||
* @returns True if changes were applied successfully
|
||||
*/
|
||||
protected async applyRenameEdit(
|
||||
view: EditorView,
|
||||
edit: LSP.WorkspaceEdit | null
|
||||
): Promise<boolean> {
|
||||
if (!edit) {
|
||||
showErrorMessage(view, 'No edit returned from language server')
|
||||
return false
|
||||
}
|
||||
|
||||
const changesMap = edit.changes ?? {}
|
||||
const documentChanges = edit.documentChanges ?? []
|
||||
|
||||
if (Object.keys(changesMap).length === 0 && documentChanges.length === 0) {
|
||||
showErrorMessage(view, 'No changes to apply')
|
||||
return false
|
||||
}
|
||||
|
||||
// Handle documentChanges (preferred) if available
|
||||
if (documentChanges.length > 0) {
|
||||
for (const docChange of documentChanges) {
|
||||
if ('textDocument' in docChange) {
|
||||
// This is a TextDocumentEdit
|
||||
const uri = docChange.textDocument.uri
|
||||
|
||||
if (uri !== this.getDocUri()) {
|
||||
showErrorMessage(view, 'Multi-file rename not supported yet')
|
||||
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
|
||||
const changes = sortedEdits.map((edit) => ({
|
||||
from: posToOffset(view.state.doc, edit.range.start) ?? 0,
|
||||
to: posToOffset(view.state.doc, edit.range.end) ?? 0,
|
||||
insert: edit.newText,
|
||||
annotations: [lspRenameEvent],
|
||||
}))
|
||||
|
||||
view.dispatch(view.state.update({ changes }))
|
||||
return true
|
||||
}
|
||||
|
||||
// This is a CreateFile, RenameFile, or DeleteFile operation
|
||||
showErrorMessage(
|
||||
view,
|
||||
'File creation, deletion, or renaming operations not supported yet'
|
||||
)
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Fall back to changes if documentChanges is not available
|
||||
else if (Object.keys(changesMap).length > 0) {
|
||||
// Apply all changes
|
||||
for (const [uri, changes] of Object.entries(changesMap)) {
|
||||
if (uri !== this.getDocUri()) {
|
||||
showErrorMessage(view, 'Multi-file rename not supported yet')
|
||||
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
|
||||
const changeSpecs = sortedChanges.map((change) => ({
|
||||
from: posToOffset(view.state.doc, change.range.start) ?? 0,
|
||||
to: posToOffset(view.state.doc, change.range.end) ?? 0,
|
||||
insert: change.newText,
|
||||
}))
|
||||
|
||||
view.dispatch(view.state.update({ changes: changeSpecs }))
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
parseSemanticTokens(view: EditorView, data: number[]) {
|
||||
// decode the lsp semantic token types
|
||||
const tokens = []
|
||||
@ -509,7 +1204,7 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
params
|
||||
)
|
||||
// this is sometimes slower than our actual typing.
|
||||
this.processDiagnostics(params)
|
||||
await this.processDiagnostics(params)
|
||||
break
|
||||
case 'window/logMessage':
|
||||
console.log(
|
||||
@ -534,25 +1229,74 @@ export class LanguageServerPlugin implements PluginValue {
|
||||
this.processLspNotification?.(this, notification)
|
||||
}
|
||||
|
||||
processDiagnostics(params: PublishDiagnosticsParams) {
|
||||
async processDiagnostics(params: PublishDiagnosticsParams) {
|
||||
if (params.uri !== this.getDocUri()) return
|
||||
|
||||
// Commented to avoid the lint. See TODO below.
|
||||
// const diagnostics =
|
||||
params.diagnostics
|
||||
.map(({ range, message, severity }) => ({
|
||||
from: posToOffset(this.view.state.doc, range.start)!,
|
||||
to: posToOffset(this.view.state.doc, range.end)!,
|
||||
severity: (
|
||||
{
|
||||
[DiagnosticSeverity.Error]: 'error',
|
||||
[DiagnosticSeverity.Warning]: 'warning',
|
||||
[DiagnosticSeverity.Information]: 'info',
|
||||
[DiagnosticSeverity.Hint]: 'info',
|
||||
} as const
|
||||
)[severity!],
|
||||
const rawDiagnostics = params.diagnostics.map(
|
||||
async ({ range, message, severity, code }) => {
|
||||
const actions = await this.requestCodeActions(range, [code as string])
|
||||
|
||||
const codemirrorActions = actions?.map(
|
||||
(action): Action => ({
|
||||
name:
|
||||
'command' in action && typeof action.command === 'object'
|
||||
? action.command?.title || action.title
|
||||
: action.title,
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
apply: async () => {
|
||||
if ('edit' in action && action.edit?.changes) {
|
||||
const changes = action.edit.changes[this.getDocUri()]
|
||||
|
||||
if (!changes) {
|
||||
return
|
||||
}
|
||||
|
||||
// Apply workspace edit
|
||||
for (const change of changes) {
|
||||
this.view.dispatch(
|
||||
this.view.state.update({
|
||||
changes: {
|
||||
from: posToOffsetOrZero(
|
||||
this.view.state.doc,
|
||||
change.range.start
|
||||
),
|
||||
to: posToOffset(this.view.state.doc, change.range.end),
|
||||
insert: change.newText,
|
||||
},
|
||||
annotations: [lspCodeActionEvent],
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if ('command' in action && action.command) {
|
||||
// TODO: Implement command execution
|
||||
// Execute command if present
|
||||
console.warn(
|
||||
'[codemirror-lsp-client/processDiagnostics] executing command:',
|
||||
action.command
|
||||
)
|
||||
}
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
const diagnostic: Diagnostic = {
|
||||
from: posToOffsetOrZero(this.view.state.doc, range.start),
|
||||
to: posToOffsetOrZero(this.view.state.doc, range.end),
|
||||
severity: severityMap[severity ?? DiagnosticSeverity.Error],
|
||||
source: this.getLanguageId(),
|
||||
actions: codemirrorActions,
|
||||
message,
|
||||
}))
|
||||
}
|
||||
|
||||
return diagnostic
|
||||
}
|
||||
)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const diagnostics = (await Promise.all(rawDiagnostics))
|
||||
.filter(
|
||||
({ from, to }) =>
|
||||
from !== null && to !== null && from !== undefined && to !== undefined
|
||||
@ -581,12 +1325,15 @@ export class LanguageServerPluginSpec
|
||||
{
|
||||
provide(plugin: ViewPlugin<LanguageServerPlugin>): Extension {
|
||||
return [
|
||||
linter(null),
|
||||
lspAutocompleteExt(plugin),
|
||||
lspFormatExt(plugin),
|
||||
lspGoToDefinitionExt(plugin),
|
||||
lspHoverExt(plugin),
|
||||
lspIndentExt(),
|
||||
lspRenameExt(plugin),
|
||||
lspSemanticTokensExt(),
|
||||
linter(null),
|
||||
lspSignatureHelpExt(plugin),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
32
packages/codemirror-lsp-client/src/plugin/rename.ts
Normal file
32
packages/codemirror-lsp-client/src/plugin/rename.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import type { Extension } from '@codemirror/state'
|
||||
import { Prec } from '@codemirror/state'
|
||||
import type { ViewPlugin } from '@codemirror/view'
|
||||
import { keymap } from '@codemirror/view'
|
||||
|
||||
import type { LanguageServerPlugin } from './lsp'
|
||||
import { offsetToPos } from './util'
|
||||
|
||||
export default function lspRenameExt(
|
||||
plugin: ViewPlugin<LanguageServerPlugin>
|
||||
): Extension {
|
||||
return [
|
||||
Prec.highest(
|
||||
keymap.of([
|
||||
{
|
||||
key: 'F2',
|
||||
run: (view) => {
|
||||
if (!plugin) return false
|
||||
|
||||
const value = view.plugin(plugin)
|
||||
if (!value) return false
|
||||
|
||||
const pos = view.state.selection.main.head
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
value.requestRename(view, offsetToPos(view.state.doc, pos))
|
||||
return true
|
||||
},
|
||||
},
|
||||
])
|
||||
),
|
||||
]
|
||||
}
|
90
packages/codemirror-lsp-client/src/plugin/signature-help.ts
Normal file
90
packages/codemirror-lsp-client/src/plugin/signature-help.ts
Normal file
@ -0,0 +1,90 @@
|
||||
import type { Extension } from '@codemirror/state'
|
||||
import { Prec } from '@codemirror/state'
|
||||
import type { ViewPlugin } from '@codemirror/view'
|
||||
import { EditorView } from '@codemirror/view'
|
||||
import { keymap } from '@codemirror/view'
|
||||
|
||||
import type { LanguageServerPlugin } from './lsp'
|
||||
|
||||
export default function lspSignatureHelpExt(
|
||||
plugin: ViewPlugin<LanguageServerPlugin>
|
||||
): Extension {
|
||||
return [
|
||||
Prec.highest(
|
||||
keymap.of([
|
||||
{
|
||||
key: 'Mod-Shift-Space',
|
||||
run: (view) => {
|
||||
if (!plugin) {
|
||||
return false
|
||||
}
|
||||
|
||||
const value = view.plugin(plugin)
|
||||
if (!value) return false
|
||||
|
||||
const pos = view.state.selection.main.head
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
value.showSignatureHelpTooltip(view, pos)
|
||||
return true
|
||||
},
|
||||
},
|
||||
])
|
||||
),
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
EditorView.updateListener.of(async (update) => {
|
||||
if (!(plugin && update.docChanged)) return
|
||||
|
||||
// Make sure this is a valid user typing event.
|
||||
let isRelevant = false
|
||||
for (const tr of update.transactions) {
|
||||
if (tr.isUserEvent('input')) {
|
||||
isRelevant = true
|
||||
}
|
||||
}
|
||||
|
||||
if (!isRelevant) {
|
||||
// We only want signature help on user events.
|
||||
return
|
||||
}
|
||||
|
||||
const value = update.view.plugin(plugin)
|
||||
if (!value) return false
|
||||
|
||||
// Early exit if signature help capability is not supported
|
||||
if (!value.client.getServerCapabilities().signatureHelpProvider) return
|
||||
|
||||
const triggerChars = value.client.getServerCapabilities()
|
||||
.signatureHelpProvider?.triggerCharacters || ['(', ',']
|
||||
let triggerCharacter: string | undefined
|
||||
|
||||
// Check if changes include trigger characters
|
||||
const changes = update.changes
|
||||
let shouldTrigger = false
|
||||
let triggerPos = -1
|
||||
|
||||
changes.iterChanges((_fromA, _toA, _fromB, toB, inserted) => {
|
||||
if (shouldTrigger) return // Skip if already found a trigger
|
||||
|
||||
const text = inserted.toString()
|
||||
if (!text) return
|
||||
|
||||
for (const char of triggerChars) {
|
||||
if (text.includes(char)) {
|
||||
shouldTrigger = true
|
||||
triggerPos = toB
|
||||
triggerCharacter = char
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (shouldTrigger && triggerPos >= 0) {
|
||||
await value.showSignatureHelpTooltip(
|
||||
update.view,
|
||||
triggerPos,
|
||||
triggerCharacter
|
||||
)
|
||||
}
|
||||
}),
|
||||
]
|
||||
}
|
@ -2,6 +2,7 @@ import type { Text } from '@codemirror/state'
|
||||
import type { MarkedOptions } from '@ts-stack/markdown'
|
||||
import { Marked } from '@ts-stack/markdown'
|
||||
import type * as LSP from 'vscode-languageserver-protocol'
|
||||
import type { EditorView } from '@codemirror/view'
|
||||
|
||||
import { isArray } from '../lib/utils'
|
||||
|
||||
@ -36,6 +37,13 @@ export function posToOffset(
|
||||
return offset
|
||||
}
|
||||
|
||||
export function posToOffsetOrZero(
|
||||
doc: Text,
|
||||
pos: { line: number; character: number }
|
||||
): number {
|
||||
return posToOffset(doc, pos) || 0
|
||||
}
|
||||
|
||||
export function offsetToPos(doc: Text, offset: number) {
|
||||
const line = doc.lineAt(offset)
|
||||
return {
|
||||
@ -48,14 +56,84 @@ const markedOptions: MarkedOptions = {
|
||||
gfm: true,
|
||||
}
|
||||
|
||||
export function formatMarkdownContents(
|
||||
contents: LSP.MarkupContent | LSP.MarkedString | LSP.MarkedString[]
|
||||
): string {
|
||||
if (isArray(contents)) {
|
||||
return contents.map((c) => formatMarkdownContents(c) + '\n\n').join('')
|
||||
} else if (typeof contents === 'string') {
|
||||
return Marked.parse(contents, markedOptions)
|
||||
} else {
|
||||
return Marked.parse(contents.value, markedOptions)
|
||||
}
|
||||
export function isLSPTextEdit(
|
||||
textEdit?: LSP.TextEdit | LSP.InsertReplaceEdit
|
||||
): textEdit is LSP.TextEdit {
|
||||
return (textEdit as LSP.TextEdit)?.range !== undefined
|
||||
}
|
||||
|
||||
export function isLSPMarkupContent(
|
||||
contents: LSP.MarkupContent | LSP.MarkedString | LSP.MarkedString[]
|
||||
): contents is LSP.MarkupContent {
|
||||
return (contents as LSP.MarkupContent).kind !== undefined
|
||||
}
|
||||
|
||||
export function formatContents(
|
||||
contents:
|
||||
| LSP.MarkupContent
|
||||
| LSP.MarkedString
|
||||
| LSP.MarkedString[]
|
||||
| undefined
|
||||
): string {
|
||||
if (!contents) {
|
||||
return ''
|
||||
}
|
||||
if (isLSPMarkupContent(contents)) {
|
||||
let value = contents.value
|
||||
if (contents.kind === 'markdown') {
|
||||
value = Marked.parse(value, markedOptions)
|
||||
}
|
||||
return value
|
||||
}
|
||||
if (isArray(contents)) {
|
||||
return contents
|
||||
.map((c) => formatContents(c))
|
||||
.filter(Boolean)
|
||||
.join('\n\n')
|
||||
}
|
||||
if (typeof contents === 'string') {
|
||||
return contents
|
||||
}
|
||||
if (
|
||||
typeof contents === 'object' &&
|
||||
'language' in contents &&
|
||||
'value' in contents
|
||||
) {
|
||||
return contents.value
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
export function showErrorMessage(view: EditorView, message: string) {
|
||||
const tooltip = document.createElement('div')
|
||||
tooltip.className = 'cm-error-message'
|
||||
tooltip.style.cssText = `
|
||||
position: absolute;
|
||||
padding: 8px;
|
||||
background: #fee;
|
||||
border: 1px solid #fcc;
|
||||
border-radius: 4px;
|
||||
color: #c00;
|
||||
font-size: 14px;
|
||||
z-index: 100;
|
||||
max-width: 300px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,.15);
|
||||
`
|
||||
tooltip.textContent = message
|
||||
|
||||
// Position near the cursor
|
||||
const cursor = view.coordsAtPos(view.state.selection.main.head)
|
||||
if (cursor) {
|
||||
tooltip.style.left = `${cursor.left}px`
|
||||
tooltip.style.top = `${cursor.bottom + 5}px`
|
||||
}
|
||||
|
||||
document.body.appendChild(tooltip)
|
||||
|
||||
// Remove after 3 seconds
|
||||
setTimeout(() => {
|
||||
tooltip.style.opacity = '0'
|
||||
tooltip.style.transition = 'opacity 0.2s'
|
||||
setTimeout(() => tooltip.remove(), 200)
|
||||
}, 3000)
|
||||
}
|
||||
|
@ -1287,6 +1287,69 @@ impl LanguageServer for Backend {
|
||||
|
||||
let pos = position_to_char_index(params.text_document_position_params.position, current_code);
|
||||
|
||||
// Get the character at the position.
|
||||
let Some(ch) = current_code.chars().nth(pos) else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let check_char = |ch: char| {
|
||||
// If we are on a (, then get the string in front of the (
|
||||
// and try to get the signature.
|
||||
// We do these before the ast check because we might not have a valid ast.
|
||||
if ch == '(' {
|
||||
// If the current character is not a " " then get the next space after
|
||||
// our position so we can split on that.
|
||||
// Find the next space after the current position.
|
||||
let next_space = if ch != ' ' {
|
||||
if let Some(next_space) = current_code[pos..].find(' ') {
|
||||
pos + next_space
|
||||
} else if let Some(next_space) = current_code[pos..].find('(') {
|
||||
pos + next_space
|
||||
} else {
|
||||
pos
|
||||
}
|
||||
} else {
|
||||
pos
|
||||
};
|
||||
let p2 = std::cmp::max(pos, next_space);
|
||||
|
||||
let last_word = current_code[..p2].split_whitespace().last()?;
|
||||
|
||||
// Get the function name.
|
||||
return self.stdlib_signatures.get(last_word);
|
||||
} else if ch == ',' {
|
||||
// If we have a comma, then get the string in front of
|
||||
// the closest ( and try to get the signature.
|
||||
|
||||
// Find the last ( before the comma.
|
||||
let last_paren = current_code[..pos].rfind('(')?;
|
||||
// Get the string in front of the (.
|
||||
let last_word = current_code[..last_paren].split_whitespace().last()?;
|
||||
// Get the function name.
|
||||
return self.stdlib_signatures.get(last_word);
|
||||
}
|
||||
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(signature) = check_char(ch) {
|
||||
return Ok(Some(signature.clone()));
|
||||
}
|
||||
|
||||
// Check if we have context.
|
||||
if let Some(context) = params.context {
|
||||
if let Some(character) = context.trigger_character {
|
||||
for character in character.chars() {
|
||||
// Check if we are on a ( or a ,.
|
||||
if character == '(' || character == ',' {
|
||||
if let Some(signature) = check_char(character) {
|
||||
return Ok(Some(signature.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Let's iterate over the AST and find the node that contains the cursor.
|
||||
let Some(ast) = self.ast_map.get(&filename) else {
|
||||
return Ok(None);
|
||||
@ -1419,7 +1482,7 @@ impl LanguageServer for Backend {
|
||||
ast.rename_symbol(¶ms.new_name, pos);
|
||||
// Now recast it.
|
||||
let recast = ast.recast(&Default::default(), 0);
|
||||
let source_range = SourceRange::new(0, current_code.len() - 1, module_id);
|
||||
let source_range = SourceRange::new(0, current_code.len(), module_id);
|
||||
let range = source_range.to_lsp_range(current_code);
|
||||
Ok(Some(WorkspaceEdit {
|
||||
changes: Some(HashMap::from([(
|
||||
@ -1590,7 +1653,7 @@ fn position_to_char_index(position: Position, code: &str) -> usize {
|
||||
}
|
||||
}
|
||||
|
||||
char_position
|
||||
std::cmp::min(char_position, code.len() - 1)
|
||||
}
|
||||
|
||||
async fn with_cached_var<T>(name: &str, f: impl Fn(&KclValue) -> T) -> Option<T> {
|
||||
|
@ -1119,6 +1119,348 @@ async fn test_kcl_lsp_signature_help() {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_kcl_lsp_signature_help_on_parens_trigger() {
|
||||
let server = kcl_lsp_server(false).await.unwrap();
|
||||
|
||||
// Send open file.
|
||||
// We do this to trigger a valid ast.
|
||||
server
|
||||
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
|
||||
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
|
||||
uri: "file:///test.kcl".try_into().unwrap(),
|
||||
version: 1,
|
||||
},
|
||||
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
|
||||
range: None,
|
||||
range_length: None,
|
||||
text: "myVarName = 100
|
||||
|
||||
a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [myVarName, 0])
|
||||
|> yLine(length = -100.0)
|
||||
|> xLine(length = -100.0)
|
||||
|> yLine(length = 100.0)
|
||||
|> close()"
|
||||
.to_string(),
|
||||
}],
|
||||
})
|
||||
.await;
|
||||
|
||||
// Send open file.
|
||||
server
|
||||
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
|
||||
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
|
||||
uri: "file:///test.kcl".try_into().unwrap(),
|
||||
version: 1,
|
||||
},
|
||||
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
|
||||
range: None,
|
||||
range_length: None,
|
||||
text: "myVarName = 100
|
||||
|
||||
a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [myVarName, 0])
|
||||
|> yLine(length = -100.0)
|
||||
|> xLine(length = -100.0)
|
||||
|> yLine(length = 100.0)
|
||||
|> close()
|
||||
|> extrude("
|
||||
.to_string(),
|
||||
}],
|
||||
})
|
||||
.await;
|
||||
|
||||
// Send signature help request.
|
||||
let signature_help = server
|
||||
.signature_help(tower_lsp::lsp_types::SignatureHelpParams {
|
||||
text_document_position_params: tower_lsp::lsp_types::TextDocumentPositionParams {
|
||||
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
|
||||
uri: "file:///test.kcl".try_into().unwrap(),
|
||||
},
|
||||
position: tower_lsp::lsp_types::Position { line: 9, character: 14 },
|
||||
},
|
||||
context: None,
|
||||
work_done_progress_params: Default::default(),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Check the signature help.
|
||||
if let Some(signature_help) = signature_help {
|
||||
assert_eq!(
|
||||
signature_help.signatures.len(),
|
||||
1,
|
||||
"Expected one signature, got {:?}",
|
||||
signature_help.signatures
|
||||
);
|
||||
assert_eq!(signature_help.signatures[0].label, "extrude");
|
||||
} else {
|
||||
panic!("Expected signature help");
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_kcl_lsp_signature_help_on_parens_trigger_on_before() {
|
||||
let server = kcl_lsp_server(false).await.unwrap();
|
||||
|
||||
// Send open file.
|
||||
// We do this to trigger a valid ast.
|
||||
server
|
||||
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
|
||||
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
|
||||
uri: "file:///test.kcl".try_into().unwrap(),
|
||||
version: 1,
|
||||
},
|
||||
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
|
||||
range: None,
|
||||
range_length: None,
|
||||
text: "myVarName = 100
|
||||
|
||||
a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [myVarName, 0])
|
||||
|> yLine(length = -100.0)
|
||||
|> xLine(length = -100.0)
|
||||
|> yLine(length = 100.0)
|
||||
|> close()"
|
||||
.to_string(),
|
||||
}],
|
||||
})
|
||||
.await;
|
||||
|
||||
// Send open file.
|
||||
server
|
||||
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
|
||||
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
|
||||
uri: "file:///test.kcl".try_into().unwrap(),
|
||||
version: 1,
|
||||
},
|
||||
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
|
||||
range: None,
|
||||
range_length: None,
|
||||
text: "myVarName = 100
|
||||
|
||||
a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [myVarName, 0])
|
||||
|> yLine(length = -100.0)
|
||||
|> xLine(length = -100.0)
|
||||
|> yLine(length = 100.0)
|
||||
|> close()
|
||||
|> extrude("
|
||||
.to_string(),
|
||||
}],
|
||||
})
|
||||
.await;
|
||||
|
||||
// Send signature help request.
|
||||
let signature_help = server
|
||||
.signature_help(tower_lsp::lsp_types::SignatureHelpParams {
|
||||
text_document_position_params: tower_lsp::lsp_types::TextDocumentPositionParams {
|
||||
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
|
||||
uri: "file:///test.kcl".try_into().unwrap(),
|
||||
},
|
||||
position: tower_lsp::lsp_types::Position { line: 9, character: 10 },
|
||||
},
|
||||
context: Some(tower_lsp::lsp_types::SignatureHelpContext {
|
||||
trigger_kind: tower_lsp::lsp_types::SignatureHelpTriggerKind::INVOKED,
|
||||
trigger_character: Some("(".to_string()),
|
||||
is_retrigger: false,
|
||||
active_signature_help: None,
|
||||
}),
|
||||
work_done_progress_params: Default::default(),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Check the signature help.
|
||||
if let Some(signature_help) = signature_help {
|
||||
assert_eq!(
|
||||
signature_help.signatures.len(),
|
||||
1,
|
||||
"Expected one signature, got {:?}",
|
||||
signature_help.signatures
|
||||
);
|
||||
assert_eq!(signature_help.signatures[0].label, "extrude");
|
||||
} else {
|
||||
panic!("Expected signature help");
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_kcl_lsp_signature_help_on_comma_trigger() {
|
||||
let server = kcl_lsp_server(false).await.unwrap();
|
||||
|
||||
// Send open file.
|
||||
// We do this to trigger a valid ast.
|
||||
server
|
||||
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
|
||||
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
|
||||
uri: "file:///test.kcl".try_into().unwrap(),
|
||||
version: 1,
|
||||
},
|
||||
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
|
||||
range: None,
|
||||
range_length: None,
|
||||
text: "myVarName = 100
|
||||
|
||||
a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [myVarName, 0])
|
||||
|> yLine(length = -100.0)
|
||||
|> xLine(length = -100.0)
|
||||
|> yLine(length = 100.0)
|
||||
|> close()"
|
||||
.to_string(),
|
||||
}],
|
||||
})
|
||||
.await;
|
||||
|
||||
// Send update file.
|
||||
server
|
||||
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
|
||||
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
|
||||
uri: "file:///test.kcl".try_into().unwrap(),
|
||||
version: 1,
|
||||
},
|
||||
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
|
||||
range: None,
|
||||
range_length: None,
|
||||
text: "myVarName = 100
|
||||
|
||||
a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [myVarName, 0])
|
||||
|> yLine(length = -100.0)
|
||||
|> xLine(length = -100.0)
|
||||
|> yLine(length = 100.0)
|
||||
|> close()
|
||||
|> extrude(length = 10,"
|
||||
.to_string(),
|
||||
}],
|
||||
})
|
||||
.await;
|
||||
|
||||
// Send signature help request.
|
||||
let signature_help = server
|
||||
.signature_help(tower_lsp::lsp_types::SignatureHelpParams {
|
||||
text_document_position_params: tower_lsp::lsp_types::TextDocumentPositionParams {
|
||||
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
|
||||
uri: "file:///test.kcl".try_into().unwrap(),
|
||||
},
|
||||
position: tower_lsp::lsp_types::Position { line: 9, character: 25 },
|
||||
},
|
||||
context: None,
|
||||
work_done_progress_params: Default::default(),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Check the signature help.
|
||||
if let Some(signature_help) = signature_help {
|
||||
assert_eq!(
|
||||
signature_help.signatures.len(),
|
||||
1,
|
||||
"Expected one signature, got {:?}",
|
||||
signature_help.signatures
|
||||
);
|
||||
assert_eq!(signature_help.signatures[0].label, "extrude");
|
||||
} else {
|
||||
panic!("Expected signature help");
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_kcl_lsp_signature_help_on_comma_trigger_on_before() {
|
||||
let server = kcl_lsp_server(false).await.unwrap();
|
||||
|
||||
// Send open file.
|
||||
// We do this to trigger a valid ast.
|
||||
server
|
||||
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
|
||||
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
|
||||
uri: "file:///test.kcl".try_into().unwrap(),
|
||||
version: 1,
|
||||
},
|
||||
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
|
||||
range: None,
|
||||
range_length: None,
|
||||
text: "myVarName = 100
|
||||
|
||||
a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [myVarName, 0])
|
||||
|> yLine(length = -100.0)
|
||||
|> xLine(length = -100.0)
|
||||
|> yLine(length = 100.0)
|
||||
|> close()"
|
||||
.to_string(),
|
||||
}],
|
||||
})
|
||||
.await;
|
||||
|
||||
// Send update file.
|
||||
server
|
||||
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
|
||||
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
|
||||
uri: "file:///test.kcl".try_into().unwrap(),
|
||||
version: 1,
|
||||
},
|
||||
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
|
||||
range: None,
|
||||
range_length: None,
|
||||
text: "myVarName = 100
|
||||
|
||||
a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [myVarName, 0])
|
||||
|> yLine(length = -100.0)
|
||||
|> xLine(length = -100.0)
|
||||
|> yLine(length = 100.0)
|
||||
|> close()
|
||||
|> extrude(length = 10,"
|
||||
.to_string(),
|
||||
}],
|
||||
})
|
||||
.await;
|
||||
|
||||
// Send signature help request.
|
||||
let signature_help = server
|
||||
.signature_help(tower_lsp::lsp_types::SignatureHelpParams {
|
||||
text_document_position_params: tower_lsp::lsp_types::TextDocumentPositionParams {
|
||||
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
|
||||
uri: "file:///test.kcl".try_into().unwrap(),
|
||||
},
|
||||
position: tower_lsp::lsp_types::Position { line: 9, character: 22 },
|
||||
},
|
||||
context: Some(tower_lsp::lsp_types::SignatureHelpContext {
|
||||
trigger_kind: tower_lsp::lsp_types::SignatureHelpTriggerKind::INVOKED,
|
||||
trigger_character: Some(",".to_string()),
|
||||
is_retrigger: false,
|
||||
active_signature_help: None,
|
||||
}),
|
||||
work_done_progress_params: Default::default(),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Check the signature help.
|
||||
if let Some(signature_help) = signature_help {
|
||||
assert_eq!(
|
||||
signature_help.signatures.len(),
|
||||
1,
|
||||
"Expected one signature, got {:?}",
|
||||
signature_help.signatures
|
||||
);
|
||||
assert_eq!(signature_help.signatures[0].label, "extrude");
|
||||
} else {
|
||||
panic!("Expected signature help");
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_kcl_lsp_semantic_tokens() {
|
||||
let server = kcl_lsp_server(false).await.unwrap();
|
||||
@ -1808,13 +2150,91 @@ async fn test_kcl_lsp_rename() {
|
||||
vec![tower_lsp::lsp_types::TextEdit {
|
||||
range: tower_lsp::lsp_types::Range {
|
||||
start: tower_lsp::lsp_types::Position { line: 0, character: 0 },
|
||||
end: tower_lsp::lsp_types::Position { line: 0, character: 7 }
|
||||
end: tower_lsp::lsp_types::Position { line: 0, character: 8 }
|
||||
},
|
||||
new_text: "newName = 1\n".to_string()
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_kcl_lsp_rename_no_hanging_parens() {
|
||||
let server = kcl_lsp_server(false).await.unwrap();
|
||||
|
||||
let code = r#"myVARName = 100
|
||||
|
||||
a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [myVARName, 0])
|
||||
|> yLine(length = -100.0)
|
||||
|> xLine(length = -100.0)
|
||||
|> yLine(length = 100.0)
|
||||
|> close()
|
||||
|> extrude(length = 3.14)"#;
|
||||
|
||||
// Send open file.
|
||||
server
|
||||
.did_open(tower_lsp::lsp_types::DidOpenTextDocumentParams {
|
||||
text_document: tower_lsp::lsp_types::TextDocumentItem {
|
||||
uri: "file:///test.kcl".try_into().unwrap(),
|
||||
language_id: "kcl".to_string(),
|
||||
version: 1,
|
||||
text: code.to_string(),
|
||||
},
|
||||
})
|
||||
.await;
|
||||
|
||||
// Send rename request.
|
||||
let rename = server
|
||||
.rename(tower_lsp::lsp_types::RenameParams {
|
||||
text_document_position: tower_lsp::lsp_types::TextDocumentPositionParams {
|
||||
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
|
||||
uri: "file:///test.kcl".try_into().unwrap(),
|
||||
},
|
||||
position: tower_lsp::lsp_types::Position { line: 0, character: 2 },
|
||||
},
|
||||
new_name: "myVarName".to_string(),
|
||||
work_done_progress_params: Default::default(),
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
// Check the rename.
|
||||
let changes = rename.changes.unwrap();
|
||||
|
||||
let last_character = 27;
|
||||
|
||||
// Get the last character of the last line of the original code.
|
||||
assert_eq!(code.lines().last().unwrap().chars().count(), last_character);
|
||||
|
||||
let u: tower_lsp::lsp_types::Url = "file:///test.kcl".try_into().unwrap();
|
||||
assert_eq!(
|
||||
changes.get(&u).unwrap().clone(),
|
||||
vec![tower_lsp::lsp_types::TextEdit {
|
||||
range: tower_lsp::lsp_types::Range {
|
||||
start: tower_lsp::lsp_types::Position { line: 0, character: 0 },
|
||||
// Its important we get back the right number here so that we actually replace the whole text!!
|
||||
end: tower_lsp::lsp_types::Position {
|
||||
line: 9,
|
||||
character: last_character as u32
|
||||
}
|
||||
},
|
||||
new_text: "myVarName = 100
|
||||
|
||||
a1 = startSketchOn(offsetPlane(XY, offset = 10))
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [myVarName, 0])
|
||||
|> yLine(length = -100.0)
|
||||
|> xLine(length = -100.0)
|
||||
|> yLine(length = 100.0)
|
||||
|> close()
|
||||
|> extrude(length = 3.14)\n"
|
||||
.to_string()
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_kcl_lsp_diagnostic_no_errors() {
|
||||
let server = kcl_lsp_server(false).await.unwrap();
|
||||
|
@ -2644,3 +2644,45 @@ mod import_mesh_clone {
|
||||
super::execute(TEST_NAME, true).await
|
||||
}
|
||||
}
|
||||
mod clone_w_fillets {
|
||||
const TEST_NAME: &str = "clone_w_fillets";
|
||||
|
||||
/// Test parsing KCL.
|
||||
#[test]
|
||||
fn parse() {
|
||||
super::parse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn unparse() {
|
||||
super::unparse(TEST_NAME).await
|
||||
}
|
||||
|
||||
/// Test that KCL is executed correctly.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_execute() {
|
||||
super::execute(TEST_NAME, true).await
|
||||
}
|
||||
}
|
||||
mod clone_w_shell {
|
||||
const TEST_NAME: &str = "clone_w_shell";
|
||||
|
||||
/// Test parsing KCL.
|
||||
#[test]
|
||||
fn parse() {
|
||||
super::parse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn unparse() {
|
||||
super::unparse(TEST_NAME).await
|
||||
}
|
||||
|
||||
/// Test that KCL is executed correctly.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_execute() {
|
||||
super::execute(TEST_NAME, true).await
|
||||
}
|
||||
}
|
||||
|
551
rust/kcl-lib/tests/clone_w_fillets/artifact_commands.snap
Normal file
551
rust/kcl-lib/tests/clone_w_fillets/artifact_commands.snap
Normal file
@ -0,0 +1,551 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Artifact commands clone_w_fillets.kcl
|
||||
---
|
||||
[
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "edge_lines_visible",
|
||||
"hidden": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "object_visible",
|
||||
"object_id": "[uuid]",
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "object_visible",
|
||||
"object_id": "[uuid]",
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"size": 60.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "enable_sketch_mode",
|
||||
"entity_id": "[uuid]",
|
||||
"ortho": false,
|
||||
"animated": false,
|
||||
"adjust_camera": false,
|
||||
"planar_normal": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "move_path_pen",
|
||||
"path": "[uuid]",
|
||||
"to": {
|
||||
"x": -10.0,
|
||||
"y": -5.0,
|
||||
"z": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "sketch_mode_disable"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "start_path"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "extend_path",
|
||||
"path": "[uuid]",
|
||||
"segment": {
|
||||
"type": "line",
|
||||
"end": {
|
||||
"x": 10.0,
|
||||
"y": -5.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"relative": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "extend_path",
|
||||
"path": "[uuid]",
|
||||
"segment": {
|
||||
"type": "line",
|
||||
"end": {
|
||||
"x": 10.0,
|
||||
"y": 5.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"relative": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "extend_path",
|
||||
"path": "[uuid]",
|
||||
"segment": {
|
||||
"type": "line",
|
||||
"end": {
|
||||
"x": -10.0,
|
||||
"y": 5.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"relative": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "close_path",
|
||||
"path_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "enable_sketch_mode",
|
||||
"entity_id": "[uuid]",
|
||||
"ortho": false,
|
||||
"animated": false,
|
||||
"adjust_camera": false,
|
||||
"planar_normal": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "extrude",
|
||||
"target": "[uuid]",
|
||||
"distance": 1.0,
|
||||
"faces": null,
|
||||
"opposite": "None"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "object_bring_to_front",
|
||||
"object_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "sketch_mode_disable"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_extrusion_face_info",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_opposite_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_opposite_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_opposite_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_opposite_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_fillet_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"radius": 2.0,
|
||||
"tolerance": 0.0000001,
|
||||
"cut_type": "fillet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_fillet_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"radius": 2.0,
|
||||
"tolerance": 0.0000001,
|
||||
"cut_type": "fillet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_fillet_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"radius": 2.0,
|
||||
"tolerance": 0.0000001,
|
||||
"cut_type": "fillet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_fillet_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"radius": 2.0,
|
||||
"tolerance": 0.0000001,
|
||||
"cut_type": "fillet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "entity_clone",
|
||||
"entity_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "entity_get_all_child_uuids",
|
||||
"entity_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "entity_get_all_child_uuids",
|
||||
"entity_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "object_bring_to_front",
|
||||
"object_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_extrusion_face_info",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "set_object_transform",
|
||||
"object_id": "[uuid]",
|
||||
"transforms": [
|
||||
{
|
||||
"translate": {
|
||||
"property": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 20.0
|
||||
},
|
||||
"set": false,
|
||||
"is_local": true
|
||||
},
|
||||
"rotate_rpy": null,
|
||||
"rotate_angle_axis": null,
|
||||
"scale": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Artifact graph flowchart clone_w_fillets.kcl
|
||||
extension: md
|
||||
snapshot_kind: binary
|
||||
---
|
@ -0,0 +1,80 @@
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph path2 [Path]
|
||||
2["Path<br>[100, 140, 0]"]
|
||||
3["Segment<br>[146, 200, 0]"]
|
||||
4["Segment<br>[206, 259, 0]"]
|
||||
5["Segment<br>[265, 319, 0]"]
|
||||
6["Segment<br>[325, 344, 0]"]
|
||||
7[Solid2d]
|
||||
end
|
||||
1["Plane<br>[77, 94, 0]"]
|
||||
8["Sweep Extrusion<br>[362, 410, 0]"]
|
||||
9[Wall]
|
||||
10[Wall]
|
||||
11[Wall]
|
||||
12[Wall]
|
||||
13["Cap Start"]
|
||||
14["Cap End"]
|
||||
15["SweepEdge Opposite"]
|
||||
16["SweepEdge Opposite"]
|
||||
17["SweepEdge Opposite"]
|
||||
18["SweepEdge Opposite"]
|
||||
19["SweepEdge Adjacent"]
|
||||
20["SweepEdge Adjacent"]
|
||||
21["SweepEdge Adjacent"]
|
||||
22["SweepEdge Adjacent"]
|
||||
23["EdgeCut Fillet<br>[416, 609, 0]"]
|
||||
24["EdgeCut Fillet<br>[416, 609, 0]"]
|
||||
25["EdgeCut Fillet<br>[416, 609, 0]"]
|
||||
26["EdgeCut Fillet<br>[416, 609, 0]"]
|
||||
1 --- 2
|
||||
2 --- 3
|
||||
2 --- 4
|
||||
2 --- 5
|
||||
2 --- 6
|
||||
2 --- 7
|
||||
2 ---- 8
|
||||
3 --- 12
|
||||
3 x--> 13
|
||||
3 --- 16
|
||||
3 --- 22
|
||||
4 --- 10
|
||||
4 x--> 13
|
||||
4 --- 15
|
||||
4 --- 20
|
||||
5 --- 9
|
||||
5 x--> 13
|
||||
5 --- 18
|
||||
5 --- 19
|
||||
6 --- 11
|
||||
6 x--> 13
|
||||
6 --- 17
|
||||
6 --- 21
|
||||
8 --- 9
|
||||
8 --- 10
|
||||
8 --- 11
|
||||
8 --- 12
|
||||
8 --- 13
|
||||
8 --- 14
|
||||
8 --- 15
|
||||
8 --- 16
|
||||
8 --- 17
|
||||
8 --- 18
|
||||
8 --- 19
|
||||
8 --- 20
|
||||
8 --- 21
|
||||
8 --- 22
|
||||
18 <--x 9
|
||||
15 <--x 10
|
||||
17 <--x 11
|
||||
16 <--x 12
|
||||
15 <--x 14
|
||||
16 <--x 14
|
||||
17 <--x 14
|
||||
18 <--x 14
|
||||
19 <--x 26
|
||||
20 <--x 25
|
||||
21 <--x 24
|
||||
22 <--x 23
|
||||
```
|
1289
rust/kcl-lib/tests/clone_w_fillets/ast.snap
Normal file
1289
rust/kcl-lib/tests/clone_w_fillets/ast.snap
Normal file
File diff suppressed because it is too large
Load Diff
26
rust/kcl-lib/tests/clone_w_fillets/input.kcl
Normal file
26
rust/kcl-lib/tests/clone_w_fillets/input.kcl
Normal file
@ -0,0 +1,26 @@
|
||||
width = 20
|
||||
length = 10
|
||||
thickness = 1
|
||||
filletRadius = 2
|
||||
|
||||
mountingPlateSketch = startSketchOn(XY)
|
||||
|> startProfile(at = [-width/2, -length/2])
|
||||
|> line(endAbsolute = [width/2, -length/2], tag = $edge1)
|
||||
|> line(endAbsolute = [width/2, length/2], tag = $edge2)
|
||||
|> line(endAbsolute = [-width/2, length/2], tag = $edge3)
|
||||
|> close(tag = $edge4)
|
||||
|
||||
mountingPlate = extrude(mountingPlateSketch, length = thickness)
|
||||
|> fillet(
|
||||
radius = filletRadius,
|
||||
tags = [
|
||||
getNextAdjacentEdge(edge1),
|
||||
getNextAdjacentEdge(edge2),
|
||||
getNextAdjacentEdge(edge3),
|
||||
getNextAdjacentEdge(edge4)
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
mountingPlate2 = clone(mountingPlate)
|
||||
|> translate(z = 20)
|
126
rust/kcl-lib/tests/clone_w_fillets/ops.snap
Normal file
126
rust/kcl-lib/tests/clone_w_fillets/ops.snap
Normal file
@ -0,0 +1,126 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Operations executed clone_w_fillets.kcl
|
||||
---
|
||||
[
|
||||
{
|
||||
"labeledArgs": {
|
||||
"planeOrSolid": {
|
||||
"value": {
|
||||
"type": "Plane",
|
||||
"artifact_id": "[uuid]"
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"name": "startSketchOn",
|
||||
"sourceRange": [],
|
||||
"type": "StdLibCall",
|
||||
"unlabeledArg": null
|
||||
},
|
||||
{
|
||||
"labeledArgs": {
|
||||
"length": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 1.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"name": "extrude",
|
||||
"sourceRange": [],
|
||||
"type": "StdLibCall",
|
||||
"unlabeledArg": {
|
||||
"value": {
|
||||
"type": "Sketch",
|
||||
"value": {
|
||||
"artifactId": "[uuid]"
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "KclStdLibCall",
|
||||
"name": "fillet",
|
||||
"unlabeledArg": {
|
||||
"value": {
|
||||
"type": "Solid",
|
||||
"value": {
|
||||
"artifactId": "[uuid]"
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
"labeledArgs": {
|
||||
"radius": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 2.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
"tags": {
|
||||
"value": {
|
||||
"type": "Array",
|
||||
"value": [
|
||||
{
|
||||
"type": "Uuid",
|
||||
"value": "[uuid]"
|
||||
},
|
||||
{
|
||||
"type": "Uuid",
|
||||
"value": "[uuid]"
|
||||
},
|
||||
{
|
||||
"type": "Uuid",
|
||||
"value": "[uuid]"
|
||||
},
|
||||
{
|
||||
"type": "Uuid",
|
||||
"value": "[uuid]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
{
|
||||
"labeledArgs": {
|
||||
"geometry": {
|
||||
"value": {
|
||||
"type": "Solid",
|
||||
"value": {
|
||||
"artifactId": "[uuid]"
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"name": "clone",
|
||||
"sourceRange": [],
|
||||
"type": "StdLibCall",
|
||||
"unlabeledArg": null
|
||||
}
|
||||
]
|
771
rust/kcl-lib/tests/clone_w_fillets/program_memory.snap
Normal file
771
rust/kcl-lib/tests/clone_w_fillets/program_memory.snap
Normal file
@ -0,0 +1,771 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Variables in memory after executing clone_w_fillets.kcl
|
||||
---
|
||||
{
|
||||
"edge1": {
|
||||
"type": "TagIdentifier",
|
||||
"type": "TagIdentifier",
|
||||
"value": "edge1"
|
||||
},
|
||||
"edge2": {
|
||||
"type": "TagIdentifier",
|
||||
"type": "TagIdentifier",
|
||||
"value": "edge2"
|
||||
},
|
||||
"edge3": {
|
||||
"type": "TagIdentifier",
|
||||
"type": "TagIdentifier",
|
||||
"value": "edge3"
|
||||
},
|
||||
"edge4": {
|
||||
"type": "TagIdentifier",
|
||||
"type": "TagIdentifier",
|
||||
"value": "edge4"
|
||||
},
|
||||
"filletRadius": {
|
||||
"type": "Number",
|
||||
"value": 2.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"length": {
|
||||
"type": "Number",
|
||||
"value": 10.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mountingPlate": {
|
||||
"type": "Solid",
|
||||
"value": {
|
||||
"type": "Solid",
|
||||
"id": "[uuid]",
|
||||
"artifactId": "[uuid]",
|
||||
"value": [
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": {
|
||||
"commentStart": 193,
|
||||
"end": 199,
|
||||
"start": 193,
|
||||
"type": "TagDeclarator",
|
||||
"value": "edge1"
|
||||
},
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": {
|
||||
"commentStart": 252,
|
||||
"end": 258,
|
||||
"start": 252,
|
||||
"type": "TagDeclarator",
|
||||
"value": "edge2"
|
||||
},
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": {
|
||||
"commentStart": 312,
|
||||
"end": 318,
|
||||
"start": 312,
|
||||
"type": "TagDeclarator",
|
||||
"value": "edge3"
|
||||
},
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": {
|
||||
"commentStart": 337,
|
||||
"end": 343,
|
||||
"start": 337,
|
||||
"type": "TagDeclarator",
|
||||
"value": "edge4"
|
||||
},
|
||||
"type": "extrudePlane"
|
||||
}
|
||||
],
|
||||
"sketch": {
|
||||
"type": "Sketch",
|
||||
"id": "[uuid]",
|
||||
"paths": [
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
-10.0,
|
||||
-5.0
|
||||
],
|
||||
"tag": {
|
||||
"commentStart": 193,
|
||||
"end": 199,
|
||||
"start": 193,
|
||||
"type": "TagDeclarator",
|
||||
"value": "edge1"
|
||||
},
|
||||
"to": [
|
||||
10.0,
|
||||
-5.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
10.0,
|
||||
-5.0
|
||||
],
|
||||
"tag": {
|
||||
"commentStart": 252,
|
||||
"end": 258,
|
||||
"start": 252,
|
||||
"type": "TagDeclarator",
|
||||
"value": "edge2"
|
||||
},
|
||||
"to": [
|
||||
10.0,
|
||||
5.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
10.0,
|
||||
5.0
|
||||
],
|
||||
"tag": {
|
||||
"commentStart": 312,
|
||||
"end": 318,
|
||||
"start": 312,
|
||||
"type": "TagDeclarator",
|
||||
"value": "edge3"
|
||||
},
|
||||
"to": [
|
||||
-10.0,
|
||||
5.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
-10.0,
|
||||
5.0
|
||||
],
|
||||
"tag": {
|
||||
"commentStart": 337,
|
||||
"end": 343,
|
||||
"start": 337,
|
||||
"type": "TagDeclarator",
|
||||
"value": "edge4"
|
||||
},
|
||||
"to": [
|
||||
-10.0,
|
||||
-5.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
}
|
||||
],
|
||||
"on": {
|
||||
"type": "plane",
|
||||
"id": "[uuid]",
|
||||
"artifactId": "[uuid]",
|
||||
"value": "XY",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0,
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
"xAxis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0,
|
||||
"units": {
|
||||
"type": "Unknown"
|
||||
}
|
||||
},
|
||||
"yAxis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0,
|
||||
"units": {
|
||||
"type": "Unknown"
|
||||
}
|
||||
}
|
||||
},
|
||||
"start": {
|
||||
"from": [
|
||||
-10.0,
|
||||
-5.0
|
||||
],
|
||||
"to": [
|
||||
-10.0,
|
||||
-5.0
|
||||
],
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"tag": null,
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"edge1": {
|
||||
"type": "TagIdentifier",
|
||||
"value": "edge1"
|
||||
},
|
||||
"edge2": {
|
||||
"type": "TagIdentifier",
|
||||
"value": "edge2"
|
||||
},
|
||||
"edge3": {
|
||||
"type": "TagIdentifier",
|
||||
"value": "edge3"
|
||||
},
|
||||
"edge4": {
|
||||
"type": "TagIdentifier",
|
||||
"value": "edge4"
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
"height": 1.0,
|
||||
"startCapId": "[uuid]",
|
||||
"endCapId": "[uuid]",
|
||||
"edgeCuts": [
|
||||
{
|
||||
"type": "fillet",
|
||||
"id": "[uuid]",
|
||||
"radius": {
|
||||
"n": 2.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"edgeId": "[uuid]",
|
||||
"tag": null
|
||||
},
|
||||
{
|
||||
"type": "fillet",
|
||||
"id": "[uuid]",
|
||||
"radius": {
|
||||
"n": 2.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"edgeId": "[uuid]",
|
||||
"tag": null
|
||||
},
|
||||
{
|
||||
"type": "fillet",
|
||||
"id": "[uuid]",
|
||||
"radius": {
|
||||
"n": 2.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"edgeId": "[uuid]",
|
||||
"tag": null
|
||||
},
|
||||
{
|
||||
"type": "fillet",
|
||||
"id": "[uuid]",
|
||||
"radius": {
|
||||
"n": 2.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"edgeId": "[uuid]",
|
||||
"tag": null
|
||||
}
|
||||
],
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"sectional": false
|
||||
}
|
||||
},
|
||||
"mountingPlate2": {
|
||||
"type": "Solid",
|
||||
"value": {
|
||||
"type": "Solid",
|
||||
"id": "[uuid]",
|
||||
"artifactId": "[uuid]",
|
||||
"value": [],
|
||||
"sketch": {
|
||||
"type": "Sketch",
|
||||
"id": "[uuid]",
|
||||
"paths": [
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
-10.0,
|
||||
-5.0
|
||||
],
|
||||
"tag": {
|
||||
"commentStart": 193,
|
||||
"end": 199,
|
||||
"start": 193,
|
||||
"type": "TagDeclarator",
|
||||
"value": "edge1"
|
||||
},
|
||||
"to": [
|
||||
10.0,
|
||||
-5.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
10.0,
|
||||
-5.0
|
||||
],
|
||||
"tag": {
|
||||
"commentStart": 252,
|
||||
"end": 258,
|
||||
"start": 252,
|
||||
"type": "TagDeclarator",
|
||||
"value": "edge2"
|
||||
},
|
||||
"to": [
|
||||
10.0,
|
||||
5.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
10.0,
|
||||
5.0
|
||||
],
|
||||
"tag": {
|
||||
"commentStart": 312,
|
||||
"end": 318,
|
||||
"start": 312,
|
||||
"type": "TagDeclarator",
|
||||
"value": "edge3"
|
||||
},
|
||||
"to": [
|
||||
-10.0,
|
||||
5.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
-10.0,
|
||||
5.0
|
||||
],
|
||||
"tag": {
|
||||
"commentStart": 337,
|
||||
"end": 343,
|
||||
"start": 337,
|
||||
"type": "TagDeclarator",
|
||||
"value": "edge4"
|
||||
},
|
||||
"to": [
|
||||
-10.0,
|
||||
-5.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
}
|
||||
],
|
||||
"on": {
|
||||
"type": "plane",
|
||||
"id": "[uuid]",
|
||||
"artifactId": "[uuid]",
|
||||
"value": "XY",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0,
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
"xAxis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0,
|
||||
"units": {
|
||||
"type": "Unknown"
|
||||
}
|
||||
},
|
||||
"yAxis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0,
|
||||
"units": {
|
||||
"type": "Unknown"
|
||||
}
|
||||
}
|
||||
},
|
||||
"start": {
|
||||
"from": [
|
||||
-10.0,
|
||||
-5.0
|
||||
],
|
||||
"to": [
|
||||
-10.0,
|
||||
-5.0
|
||||
],
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"tag": null,
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"edge1": {
|
||||
"type": "TagIdentifier",
|
||||
"value": "edge1"
|
||||
},
|
||||
"edge2": {
|
||||
"type": "TagIdentifier",
|
||||
"value": "edge2"
|
||||
},
|
||||
"edge3": {
|
||||
"type": "TagIdentifier",
|
||||
"value": "edge3"
|
||||
},
|
||||
"edge4": {
|
||||
"type": "TagIdentifier",
|
||||
"value": "edge4"
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
"height": 1.0,
|
||||
"startCapId": null,
|
||||
"endCapId": null,
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"sectional": false
|
||||
}
|
||||
},
|
||||
"mountingPlateSketch": {
|
||||
"type": "Sketch",
|
||||
"value": {
|
||||
"type": "Sketch",
|
||||
"id": "[uuid]",
|
||||
"paths": [
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
-10.0,
|
||||
-5.0
|
||||
],
|
||||
"tag": {
|
||||
"commentStart": 193,
|
||||
"end": 199,
|
||||
"start": 193,
|
||||
"type": "TagDeclarator",
|
||||
"value": "edge1"
|
||||
},
|
||||
"to": [
|
||||
10.0,
|
||||
-5.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
10.0,
|
||||
-5.0
|
||||
],
|
||||
"tag": {
|
||||
"commentStart": 252,
|
||||
"end": 258,
|
||||
"start": 252,
|
||||
"type": "TagDeclarator",
|
||||
"value": "edge2"
|
||||
},
|
||||
"to": [
|
||||
10.0,
|
||||
5.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
10.0,
|
||||
5.0
|
||||
],
|
||||
"tag": {
|
||||
"commentStart": 312,
|
||||
"end": 318,
|
||||
"start": 312,
|
||||
"type": "TagDeclarator",
|
||||
"value": "edge3"
|
||||
},
|
||||
"to": [
|
||||
-10.0,
|
||||
5.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
-10.0,
|
||||
5.0
|
||||
],
|
||||
"tag": {
|
||||
"commentStart": 337,
|
||||
"end": 343,
|
||||
"start": 337,
|
||||
"type": "TagDeclarator",
|
||||
"value": "edge4"
|
||||
},
|
||||
"to": [
|
||||
-10.0,
|
||||
-5.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
}
|
||||
],
|
||||
"on": {
|
||||
"type": "plane",
|
||||
"id": "[uuid]",
|
||||
"artifactId": "[uuid]",
|
||||
"value": "XY",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0,
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
"xAxis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0,
|
||||
"units": {
|
||||
"type": "Unknown"
|
||||
}
|
||||
},
|
||||
"yAxis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0,
|
||||
"units": {
|
||||
"type": "Unknown"
|
||||
}
|
||||
}
|
||||
},
|
||||
"start": {
|
||||
"from": [
|
||||
-10.0,
|
||||
-5.0
|
||||
],
|
||||
"to": [
|
||||
-10.0,
|
||||
-5.0
|
||||
],
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"tag": null,
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"edge1": {
|
||||
"type": "TagIdentifier",
|
||||
"value": "edge1"
|
||||
},
|
||||
"edge2": {
|
||||
"type": "TagIdentifier",
|
||||
"value": "edge2"
|
||||
},
|
||||
"edge3": {
|
||||
"type": "TagIdentifier",
|
||||
"value": "edge3"
|
||||
},
|
||||
"edge4": {
|
||||
"type": "TagIdentifier",
|
||||
"value": "edge4"
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
}
|
||||
},
|
||||
"thickness": {
|
||||
"type": "Number",
|
||||
"value": 1.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"width": {
|
||||
"type": "Number",
|
||||
"value": 20.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
BIN
rust/kcl-lib/tests/clone_w_fillets/rendered_model.png
Normal file
BIN
rust/kcl-lib/tests/clone_w_fillets/rendered_model.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 46 KiB |
29
rust/kcl-lib/tests/clone_w_fillets/unparsed.snap
Normal file
29
rust/kcl-lib/tests/clone_w_fillets/unparsed.snap
Normal file
@ -0,0 +1,29 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Result of unparsing clone_w_fillets.kcl
|
||||
---
|
||||
width = 20
|
||||
length = 10
|
||||
thickness = 1
|
||||
filletRadius = 2
|
||||
|
||||
mountingPlateSketch = startSketchOn(XY)
|
||||
|> startProfile(at = [-width / 2, -length / 2])
|
||||
|> line(endAbsolute = [width / 2, -length / 2], tag = $edge1)
|
||||
|> line(endAbsolute = [width / 2, length / 2], tag = $edge2)
|
||||
|> line(endAbsolute = [-width / 2, length / 2], tag = $edge3)
|
||||
|> close(tag = $edge4)
|
||||
|
||||
mountingPlate = extrude(mountingPlateSketch, length = thickness)
|
||||
|> fillet(
|
||||
radius = filletRadius,
|
||||
tags = [
|
||||
getNextAdjacentEdge(edge1),
|
||||
getNextAdjacentEdge(edge2),
|
||||
getNextAdjacentEdge(edge3),
|
||||
getNextAdjacentEdge(edge4)
|
||||
],
|
||||
)
|
||||
|
||||
mountingPlate2 = clone(mountingPlate)
|
||||
|> translate(z = 20)
|
476
rust/kcl-lib/tests/clone_w_shell/artifact_commands.snap
Normal file
476
rust/kcl-lib/tests/clone_w_shell/artifact_commands.snap
Normal file
@ -0,0 +1,476 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Artifact commands clone_w_shell.kcl
|
||||
---
|
||||
[
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "edge_lines_visible",
|
||||
"hidden": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "object_visible",
|
||||
"object_id": "[uuid]",
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "object_visible",
|
||||
"object_id": "[uuid]",
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"size": 60.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "enable_sketch_mode",
|
||||
"entity_id": "[uuid]",
|
||||
"ortho": false,
|
||||
"animated": false,
|
||||
"adjust_camera": false,
|
||||
"planar_normal": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "move_path_pen",
|
||||
"path": "[uuid]",
|
||||
"to": {
|
||||
"x": -12.0,
|
||||
"y": 12.0,
|
||||
"z": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "sketch_mode_disable"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "start_path"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "extend_path",
|
||||
"path": "[uuid]",
|
||||
"segment": {
|
||||
"type": "line",
|
||||
"end": {
|
||||
"x": 24.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"relative": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "extend_path",
|
||||
"path": "[uuid]",
|
||||
"segment": {
|
||||
"type": "line",
|
||||
"end": {
|
||||
"x": 0.0,
|
||||
"y": -24.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"relative": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "extend_path",
|
||||
"path": "[uuid]",
|
||||
"segment": {
|
||||
"type": "line",
|
||||
"end": {
|
||||
"x": -24.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"relative": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "close_path",
|
||||
"path_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "enable_sketch_mode",
|
||||
"entity_id": "[uuid]",
|
||||
"ortho": false,
|
||||
"animated": false,
|
||||
"adjust_camera": false,
|
||||
"planar_normal": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "extrude",
|
||||
"target": "[uuid]",
|
||||
"distance": 6.0,
|
||||
"faces": null,
|
||||
"opposite": "None"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "object_bring_to_front",
|
||||
"object_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "sketch_mode_disable"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_all_edge_faces",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_extrusion_face_info",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_opposite_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_opposite_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_opposite_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_opposite_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_shell_face",
|
||||
"object_id": "[uuid]",
|
||||
"face_ids": [
|
||||
"[uuid]"
|
||||
],
|
||||
"shell_thickness": 0.25,
|
||||
"hollow": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "entity_clone",
|
||||
"entity_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "entity_get_all_child_uuids",
|
||||
"entity_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "entity_get_all_child_uuids",
|
||||
"entity_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "object_bring_to_front",
|
||||
"object_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_extrusion_face_info",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "set_object_transform",
|
||||
"object_id": "[uuid]",
|
||||
"transforms": [
|
||||
{
|
||||
"translate": {
|
||||
"property": {
|
||||
"x": 50.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"set": false,
|
||||
"is_local": true
|
||||
},
|
||||
"rotate_rpy": null,
|
||||
"rotate_angle_axis": null,
|
||||
"scale": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Artifact graph flowchart clone_w_shell.kcl
|
||||
extension: md
|
||||
snapshot_kind: binary
|
||||
---
|
@ -0,0 +1,80 @@
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph path2 [Path]
|
||||
2["Path<br>[81, 109, 0]"]
|
||||
3["Segment<br>[117, 136, 0]"]
|
||||
4["Segment<br>[144, 164, 0]"]
|
||||
5["Segment<br>[172, 192, 0]"]
|
||||
6["Segment<br>[200, 207, 0]"]
|
||||
7[Solid2d]
|
||||
end
|
||||
1["Plane<br>[56, 73, 0]"]
|
||||
8["Sweep Extrusion<br>[215, 234, 0]"]
|
||||
9[Wall]
|
||||
10[Wall]
|
||||
11[Wall]
|
||||
12[Wall]
|
||||
13["Cap Start"]
|
||||
14["Cap End"]
|
||||
15["SweepEdge Opposite"]
|
||||
16["SweepEdge Opposite"]
|
||||
17["SweepEdge Opposite"]
|
||||
18["SweepEdge Opposite"]
|
||||
19["SweepEdge Adjacent"]
|
||||
20["SweepEdge Adjacent"]
|
||||
21["SweepEdge Adjacent"]
|
||||
22["SweepEdge Adjacent"]
|
||||
1 --- 2
|
||||
2 --- 3
|
||||
2 --- 4
|
||||
2 --- 5
|
||||
2 --- 6
|
||||
2 --- 7
|
||||
2 ---- 8
|
||||
3 --- 12
|
||||
3 x--> 13
|
||||
3 --- 17
|
||||
3 --- 20
|
||||
4 --- 10
|
||||
4 x--> 13
|
||||
4 --- 18
|
||||
4 --- 21
|
||||
5 --- 9
|
||||
5 x--> 13
|
||||
5 --- 15
|
||||
5 --- 22
|
||||
6 --- 11
|
||||
6 x--> 13
|
||||
6 --- 16
|
||||
6 --- 19
|
||||
8 --- 9
|
||||
8 --- 10
|
||||
8 --- 11
|
||||
8 --- 12
|
||||
8 --- 13
|
||||
8 --- 14
|
||||
8 --- 15
|
||||
8 --- 16
|
||||
8 --- 17
|
||||
8 --- 18
|
||||
8 --- 19
|
||||
8 --- 20
|
||||
8 --- 21
|
||||
8 --- 22
|
||||
15 <--x 9
|
||||
21 <--x 9
|
||||
22 <--x 9
|
||||
18 <--x 10
|
||||
20 <--x 10
|
||||
21 <--x 10
|
||||
16 <--x 11
|
||||
19 <--x 11
|
||||
22 <--x 11
|
||||
17 <--x 12
|
||||
19 <--x 12
|
||||
20 <--x 12
|
||||
15 <--x 14
|
||||
16 <--x 14
|
||||
17 <--x 14
|
||||
18 <--x 14
|
||||
```
|
732
rust/kcl-lib/tests/clone_w_shell/ast.snap
Normal file
732
rust/kcl-lib/tests/clone_w_shell/ast.snap
Normal file
@ -0,0 +1,732 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Result of parsing clone_w_shell.kcl
|
||||
---
|
||||
{
|
||||
"Ok": {
|
||||
"body": [
|
||||
{
|
||||
"commentStart": 0,
|
||||
"declaration": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"id": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "firstSketch",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"body": [
|
||||
{
|
||||
"arguments": [
|
||||
{
|
||||
"abs_path": false,
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "XY",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 0,
|
||||
"type": "Name",
|
||||
"type": "Name"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"abs_path": false,
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "startSketchOn",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 0,
|
||||
"type": "Name"
|
||||
},
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
{
|
||||
"type": "LabeledArg",
|
||||
"label": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "at",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"arg": {
|
||||
"commentStart": 0,
|
||||
"elements": [
|
||||
{
|
||||
"argument": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"raw": "12",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 12.0,
|
||||
"suffix": "None"
|
||||
}
|
||||
},
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"operator": "-",
|
||||
"start": 0,
|
||||
"type": "UnaryExpression",
|
||||
"type": "UnaryExpression"
|
||||
},
|
||||
{
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"raw": "12",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 12.0,
|
||||
"suffix": "None"
|
||||
}
|
||||
}
|
||||
],
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "ArrayExpression",
|
||||
"type": "ArrayExpression"
|
||||
}
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"abs_path": false,
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "startProfile",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 0,
|
||||
"type": "Name"
|
||||
},
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "CallExpressionKw",
|
||||
"type": "CallExpressionKw",
|
||||
"unlabeled": null
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
{
|
||||
"type": "LabeledArg",
|
||||
"label": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "end",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"arg": {
|
||||
"commentStart": 0,
|
||||
"elements": [
|
||||
{
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"raw": "24",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 24.0,
|
||||
"suffix": "None"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"raw": "0",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 0.0,
|
||||
"suffix": "None"
|
||||
}
|
||||
}
|
||||
],
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "ArrayExpression",
|
||||
"type": "ArrayExpression"
|
||||
}
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"abs_path": false,
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "line",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 0,
|
||||
"type": "Name"
|
||||
},
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "CallExpressionKw",
|
||||
"type": "CallExpressionKw",
|
||||
"unlabeled": null
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
{
|
||||
"type": "LabeledArg",
|
||||
"label": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "end",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"arg": {
|
||||
"commentStart": 0,
|
||||
"elements": [
|
||||
{
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"raw": "0",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 0.0,
|
||||
"suffix": "None"
|
||||
}
|
||||
},
|
||||
{
|
||||
"argument": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"raw": "24",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 24.0,
|
||||
"suffix": "None"
|
||||
}
|
||||
},
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"operator": "-",
|
||||
"start": 0,
|
||||
"type": "UnaryExpression",
|
||||
"type": "UnaryExpression"
|
||||
}
|
||||
],
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "ArrayExpression",
|
||||
"type": "ArrayExpression"
|
||||
}
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"abs_path": false,
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "line",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 0,
|
||||
"type": "Name"
|
||||
},
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "CallExpressionKw",
|
||||
"type": "CallExpressionKw",
|
||||
"unlabeled": null
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
{
|
||||
"type": "LabeledArg",
|
||||
"label": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "end",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"arg": {
|
||||
"commentStart": 0,
|
||||
"elements": [
|
||||
{
|
||||
"argument": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"raw": "24",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 24.0,
|
||||
"suffix": "None"
|
||||
}
|
||||
},
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"operator": "-",
|
||||
"start": 0,
|
||||
"type": "UnaryExpression",
|
||||
"type": "UnaryExpression"
|
||||
},
|
||||
{
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"raw": "0",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 0.0,
|
||||
"suffix": "None"
|
||||
}
|
||||
}
|
||||
],
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "ArrayExpression",
|
||||
"type": "ArrayExpression"
|
||||
}
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"abs_path": false,
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "line",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 0,
|
||||
"type": "Name"
|
||||
},
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "CallExpressionKw",
|
||||
"type": "CallExpressionKw",
|
||||
"unlabeled": null
|
||||
},
|
||||
{
|
||||
"arguments": [],
|
||||
"callee": {
|
||||
"abs_path": false,
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "close",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 0,
|
||||
"type": "Name"
|
||||
},
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
{
|
||||
"type": "LabeledArg",
|
||||
"label": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "length",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"arg": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"raw": "6",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 6.0,
|
||||
"suffix": "None"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"abs_path": false,
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "extrude",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 0,
|
||||
"type": "Name"
|
||||
},
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "CallExpressionKw",
|
||||
"type": "CallExpressionKw",
|
||||
"unlabeled": null
|
||||
}
|
||||
],
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"nonCodeMeta": {
|
||||
"nonCodeNodes": {
|
||||
"6": [
|
||||
{
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLineBlockComment",
|
||||
"value": "Remove the end face for the extrusion.",
|
||||
"style": "line"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"startNodes": []
|
||||
},
|
||||
"start": 0,
|
||||
"type": "PipeExpression",
|
||||
"type": "PipeExpression"
|
||||
},
|
||||
"start": 0,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 0,
|
||||
"kind": "const",
|
||||
"preComments": [
|
||||
"// Remove the end face for the extrusion."
|
||||
],
|
||||
"start": 0,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"commentStart": 0,
|
||||
"declaration": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"id": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "firstShell",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"arguments": [
|
||||
{
|
||||
"type": "LabeledArg",
|
||||
"label": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "faces",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"arg": {
|
||||
"commentStart": 0,
|
||||
"elements": [
|
||||
{
|
||||
"abs_path": false,
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "END",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 0,
|
||||
"type": "Name",
|
||||
"type": "Name"
|
||||
}
|
||||
],
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "ArrayExpression",
|
||||
"type": "ArrayExpression"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "LabeledArg",
|
||||
"label": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "thickness",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"arg": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"raw": "0.25",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 0.25,
|
||||
"suffix": "None"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"abs_path": false,
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "shell",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 0,
|
||||
"type": "Name"
|
||||
},
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "CallExpressionKw",
|
||||
"type": "CallExpressionKw",
|
||||
"unlabeled": {
|
||||
"abs_path": false,
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "firstSketch",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 0,
|
||||
"type": "Name",
|
||||
"type": "Name"
|
||||
}
|
||||
},
|
||||
"start": 0,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 0,
|
||||
"kind": "const",
|
||||
"start": 0,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"commentStart": 0,
|
||||
"declaration": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"id": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "secondShell",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"body": [
|
||||
{
|
||||
"arguments": [
|
||||
{
|
||||
"abs_path": false,
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "firstShell",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 0,
|
||||
"type": "Name",
|
||||
"type": "Name"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"abs_path": false,
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "clone",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 0,
|
||||
"type": "Name"
|
||||
},
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
{
|
||||
"type": "LabeledArg",
|
||||
"label": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "x",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"arg": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"raw": "50",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 50.0,
|
||||
"suffix": "None"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"abs_path": false,
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": {
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"name": "translate",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 0,
|
||||
"type": "Name"
|
||||
},
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "CallExpressionKw",
|
||||
"type": "CallExpressionKw",
|
||||
"unlabeled": null
|
||||
}
|
||||
],
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "PipeExpression",
|
||||
"type": "PipeExpression"
|
||||
},
|
||||
"start": 0,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 0,
|
||||
"kind": "const",
|
||||
"start": 0,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
}
|
||||
],
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"nonCodeMeta": {
|
||||
"nonCodeNodes": {
|
||||
"1": [
|
||||
{
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLine"
|
||||
}
|
||||
}
|
||||
],
|
||||
"2": [
|
||||
{
|
||||
"commentStart": 0,
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLine"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"startNodes": []
|
||||
},
|
||||
"start": 0
|
||||
}
|
||||
}
|
19
rust/kcl-lib/tests/clone_w_shell/input.kcl
Normal file
19
rust/kcl-lib/tests/clone_w_shell/input.kcl
Normal file
@ -0,0 +1,19 @@
|
||||
// Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn(XY)
|
||||
|> startProfile(at = [-12, 12])
|
||||
|> line(end = [24, 0])
|
||||
|> line(end = [0, -24])
|
||||
|> line(end = [-24, 0])
|
||||
|> close()
|
||||
|> extrude(length = 6)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
firstShell = shell(
|
||||
firstSketch,
|
||||
faces = [END],
|
||||
thickness = 0.25,
|
||||
)
|
||||
|
||||
secondShell = clone(firstShell)
|
||||
|> translate(x = 50)
|
||||
|
119
rust/kcl-lib/tests/clone_w_shell/ops.snap
Normal file
119
rust/kcl-lib/tests/clone_w_shell/ops.snap
Normal file
@ -0,0 +1,119 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Operations executed clone_w_shell.kcl
|
||||
---
|
||||
[
|
||||
{
|
||||
"labeledArgs": {
|
||||
"planeOrSolid": {
|
||||
"value": {
|
||||
"type": "Plane",
|
||||
"artifact_id": "[uuid]"
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"name": "startSketchOn",
|
||||
"sourceRange": [],
|
||||
"type": "StdLibCall",
|
||||
"unlabeledArg": null
|
||||
},
|
||||
{
|
||||
"labeledArgs": {
|
||||
"length": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 6.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"name": "extrude",
|
||||
"sourceRange": [],
|
||||
"type": "StdLibCall",
|
||||
"unlabeledArg": {
|
||||
"value": {
|
||||
"type": "Sketch",
|
||||
"value": {
|
||||
"artifactId": "[uuid]"
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "KclStdLibCall",
|
||||
"name": "shell",
|
||||
"unlabeledArg": {
|
||||
"value": {
|
||||
"type": "Array",
|
||||
"value": [
|
||||
{
|
||||
"type": "Solid",
|
||||
"value": {
|
||||
"artifactId": "[uuid]"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
"labeledArgs": {
|
||||
"faces": {
|
||||
"value": {
|
||||
"type": "Array",
|
||||
"value": [
|
||||
{
|
||||
"type": "String",
|
||||
"value": "end"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
"thickness": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 0.25,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
},
|
||||
{
|
||||
"labeledArgs": {
|
||||
"geometry": {
|
||||
"value": {
|
||||
"type": "Solid",
|
||||
"value": {
|
||||
"artifactId": "[uuid]"
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"name": "clone",
|
||||
"sourceRange": [],
|
||||
"type": "StdLibCall",
|
||||
"unlabeledArg": null
|
||||
}
|
||||
]
|
517
rust/kcl-lib/tests/clone_w_shell/program_memory.snap
Normal file
517
rust/kcl-lib/tests/clone_w_shell/program_memory.snap
Normal file
@ -0,0 +1,517 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Variables in memory after executing clone_w_shell.kcl
|
||||
---
|
||||
{
|
||||
"firstShell": {
|
||||
"type": "Solid",
|
||||
"value": {
|
||||
"type": "Solid",
|
||||
"id": "[uuid]",
|
||||
"artifactId": "[uuid]",
|
||||
"value": [
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
}
|
||||
],
|
||||
"sketch": {
|
||||
"type": "Sketch",
|
||||
"id": "[uuid]",
|
||||
"paths": [
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
-12.0,
|
||||
12.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
12.0,
|
||||
12.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
12.0,
|
||||
12.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
12.0,
|
||||
-12.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
12.0,
|
||||
-12.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
-12.0,
|
||||
-12.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
-12.0,
|
||||
-12.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
-12.0,
|
||||
12.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
}
|
||||
],
|
||||
"on": {
|
||||
"type": "plane",
|
||||
"id": "[uuid]",
|
||||
"artifactId": "[uuid]",
|
||||
"value": "XY",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0,
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
"xAxis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0,
|
||||
"units": {
|
||||
"type": "Unknown"
|
||||
}
|
||||
},
|
||||
"yAxis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0,
|
||||
"units": {
|
||||
"type": "Unknown"
|
||||
}
|
||||
}
|
||||
},
|
||||
"start": {
|
||||
"from": [
|
||||
-12.0,
|
||||
12.0
|
||||
],
|
||||
"to": [
|
||||
-12.0,
|
||||
12.0
|
||||
],
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"tag": null,
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
"height": 6.0,
|
||||
"startCapId": "[uuid]",
|
||||
"endCapId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"sectional": false
|
||||
}
|
||||
},
|
||||
"firstSketch": {
|
||||
"type": "Solid",
|
||||
"value": {
|
||||
"type": "Solid",
|
||||
"id": "[uuid]",
|
||||
"artifactId": "[uuid]",
|
||||
"value": [
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
}
|
||||
],
|
||||
"sketch": {
|
||||
"type": "Sketch",
|
||||
"id": "[uuid]",
|
||||
"paths": [
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
-12.0,
|
||||
12.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
12.0,
|
||||
12.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
12.0,
|
||||
12.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
12.0,
|
||||
-12.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
12.0,
|
||||
-12.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
-12.0,
|
||||
-12.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
-12.0,
|
||||
-12.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
-12.0,
|
||||
12.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
}
|
||||
],
|
||||
"on": {
|
||||
"type": "plane",
|
||||
"id": "[uuid]",
|
||||
"artifactId": "[uuid]",
|
||||
"value": "XY",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0,
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
"xAxis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0,
|
||||
"units": {
|
||||
"type": "Unknown"
|
||||
}
|
||||
},
|
||||
"yAxis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0,
|
||||
"units": {
|
||||
"type": "Unknown"
|
||||
}
|
||||
}
|
||||
},
|
||||
"start": {
|
||||
"from": [
|
||||
-12.0,
|
||||
12.0
|
||||
],
|
||||
"to": [
|
||||
-12.0,
|
||||
12.0
|
||||
],
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"tag": null,
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
"height": 6.0,
|
||||
"startCapId": "[uuid]",
|
||||
"endCapId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"sectional": false
|
||||
}
|
||||
},
|
||||
"secondShell": {
|
||||
"type": "Solid",
|
||||
"value": {
|
||||
"type": "Solid",
|
||||
"id": "[uuid]",
|
||||
"artifactId": "[uuid]",
|
||||
"value": [],
|
||||
"sketch": {
|
||||
"type": "Sketch",
|
||||
"id": "[uuid]",
|
||||
"paths": [
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
-12.0,
|
||||
12.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
12.0,
|
||||
12.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
12.0,
|
||||
12.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
12.0,
|
||||
-12.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
12.0,
|
||||
-12.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
-12.0,
|
||||
-12.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
-12.0,
|
||||
-12.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
-12.0,
|
||||
12.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
}
|
||||
],
|
||||
"on": {
|
||||
"type": "plane",
|
||||
"id": "[uuid]",
|
||||
"artifactId": "[uuid]",
|
||||
"value": "XY",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0,
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
"xAxis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0,
|
||||
"units": {
|
||||
"type": "Unknown"
|
||||
}
|
||||
},
|
||||
"yAxis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0,
|
||||
"units": {
|
||||
"type": "Unknown"
|
||||
}
|
||||
}
|
||||
},
|
||||
"start": {
|
||||
"from": [
|
||||
-12.0,
|
||||
12.0
|
||||
],
|
||||
"to": [
|
||||
-12.0,
|
||||
12.0
|
||||
],
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"tag": null,
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
"height": 6.0,
|
||||
"startCapId": null,
|
||||
"endCapId": null,
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"sectional": false
|
||||
}
|
||||
}
|
||||
}
|
BIN
rust/kcl-lib/tests/clone_w_shell/rendered_model.png
Normal file
BIN
rust/kcl-lib/tests/clone_w_shell/rendered_model.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
18
rust/kcl-lib/tests/clone_w_shell/unparsed.snap
Normal file
18
rust/kcl-lib/tests/clone_w_shell/unparsed.snap
Normal file
@ -0,0 +1,18 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Result of unparsing clone_w_shell.kcl
|
||||
---
|
||||
// Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn(XY)
|
||||
|> startProfile(at = [-12, 12])
|
||||
|> line(end = [24, 0])
|
||||
|> line(end = [0, -24])
|
||||
|> line(end = [-24, 0])
|
||||
|> close()
|
||||
|> extrude(length = 6)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
firstShell = shell(firstSketch, faces = [END], thickness = 0.25)
|
||||
|
||||
secondShell = clone(firstShell)
|
||||
|> translate(x = 50)
|
@ -5,7 +5,12 @@ import type {
|
||||
LanguageServerClient,
|
||||
LanguageServerOptions,
|
||||
} from '@kittycad/codemirror-lsp-client'
|
||||
import { lspFormatCodeEvent, lspPlugin } from '@kittycad/codemirror-lsp-client'
|
||||
import {
|
||||
lspCodeActionEvent,
|
||||
lspFormatCodeEvent,
|
||||
lspPlugin,
|
||||
lspRenameEvent,
|
||||
} from '@kittycad/codemirror-lsp-client'
|
||||
import { updateOutsideEditorEvent } from '@src/editor/manager'
|
||||
import { codeManagerUpdateEvent } from '@src/lang/codeManager'
|
||||
import { codeManager, editorManager, kclManager } from '@src/lib/singletons'
|
||||
@ -90,6 +95,16 @@ export class KclPlugin implements PluginValue {
|
||||
// We want to ignore other events outside the editor.
|
||||
isRelevant = false
|
||||
break
|
||||
} else if (tr.annotation(lspRenameEvent.type)) {
|
||||
// Rename does not need to trigger the world.
|
||||
// It's the same ast just different variable names.
|
||||
isRelevant = false
|
||||
break
|
||||
} else if (tr.annotation(lspCodeActionEvent.type)) {
|
||||
// Code actions should be stable enough where they create the same
|
||||
// code and we do not need to need trigger an update.
|
||||
isRelevant = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,6 +206,11 @@ code {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.cm-rename-popup {
|
||||
/* we want to overpower anything else */
|
||||
z-index: 99999999999 !important;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0%,
|
||||
100% {
|
||||
@ -220,18 +225,22 @@ code {
|
||||
@apply bg-transparent !important;
|
||||
}
|
||||
|
||||
#code-mirror-override .cm-tooltip {
|
||||
#code-mirror-override .cm-tooltip,
|
||||
.cm-tooltip {
|
||||
@apply text-xs shadow-md;
|
||||
@apply bg-chalkboard-10 text-chalkboard-80;
|
||||
@apply rounded-sm border-solid border border-chalkboard-40/30 border-l-liquid-10;
|
||||
}
|
||||
|
||||
.dark #code-mirror-override .cm-tooltip {
|
||||
.dark #code-mirror-override .cm-tooltip,
|
||||
.cm-tooltip {
|
||||
@apply bg-chalkboard-110 text-chalkboard-40;
|
||||
@apply border-chalkboard-70/20 border-l-liquid-70;
|
||||
}
|
||||
|
||||
#code-mirror-override .cm-tooltip-hover {
|
||||
#code-mirror-override .cm-tooltip-hover,
|
||||
#code-mirror-override .cm-signature-tooltip,
|
||||
.cm-signature-tooltip {
|
||||
@apply py-1 px-2 w-max max-w-md;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
import type { Diagnostic as CodeMirrorDiagnostic } from '@codemirror/lint'
|
||||
import type { Text } from '@codemirror/state'
|
||||
import { posToOffset } from '@kittycad/codemirror-lsp-client'
|
||||
import {
|
||||
lspCodeActionEvent,
|
||||
posToOffset,
|
||||
} from '@kittycad/codemirror-lsp-client'
|
||||
import type { EditorView } from 'codemirror'
|
||||
import type { Diagnostic as LspDiagnostic } from 'vscode-languageserver-protocol'
|
||||
|
||||
@ -343,6 +346,7 @@ export function compilationErrorsToDiagnostics(
|
||||
to: suggestion.source_range[1],
|
||||
insert: suggestion.insert,
|
||||
},
|
||||
annotations: [lspCodeActionEvent],
|
||||
})
|
||||
},
|
||||
},
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { Diagnostic } from '@codemirror/lint'
|
||||
|
||||
import { lspCodeActionEvent } from '@kittycad/codemirror-lsp-client'
|
||||
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
||||
|
||||
import { KCLError } from '@src/lang/errors'
|
||||
@ -8,6 +8,7 @@ import { emptyExecState, kclLint } from '@src/lang/wasm'
|
||||
import { EXECUTE_AST_INTERRUPT_ERROR_STRING } from '@src/lib/constants'
|
||||
import type RustContext from '@src/lib/rustContext'
|
||||
import { jsAppSettings } from '@src/lib/settings/settingsUtils'
|
||||
import type { EditorView } from 'codemirror'
|
||||
|
||||
export type ToolTip =
|
||||
| 'lineTo'
|
||||
@ -143,11 +144,31 @@ export async function lintAst({
|
||||
try {
|
||||
const discovered_findings = await kclLint(ast)
|
||||
return discovered_findings.map((lint) => {
|
||||
let actions
|
||||
const suggestion = lint.suggestion
|
||||
if (suggestion) {
|
||||
actions = [
|
||||
{
|
||||
name: suggestion.title,
|
||||
apply: (view: EditorView, from: number, to: number) => {
|
||||
view.dispatch({
|
||||
changes: {
|
||||
from: suggestion.source_range[0],
|
||||
to: suggestion.source_range[1],
|
||||
insert: suggestion.insert,
|
||||
},
|
||||
annotations: [lspCodeActionEvent],
|
||||
})
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
return {
|
||||
message: lint.finding.title,
|
||||
severity: 'info',
|
||||
from: lint.pos[0],
|
||||
to: lint.pos[1],
|
||||
message: lint.finding.title,
|
||||
severity: 'info',
|
||||
actions,
|
||||
}
|
||||
})
|
||||
} catch (e: any) {
|
||||
|
Reference in New Issue
Block a user