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 ({
|
test('if you click the format button it formats your code and executes so lints are still there', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
@ -481,6 +543,113 @@ sketch_001 = startSketchOn(XY)
|
|||||||
).toBeVisible()
|
).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 ({
|
test('if you write kcl with lint errors you get lints', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
|
@ -14,6 +14,7 @@ interface LSPRequestMap {
|
|||||||
LSP.CompletionParams,
|
LSP.CompletionParams,
|
||||||
LSP.CompletionItem[] | LSP.CompletionList | null,
|
LSP.CompletionItem[] | LSP.CompletionList | null,
|
||||||
]
|
]
|
||||||
|
'completionItem/resolve': [LSP.CompletionItem, LSP.CompletionItem]
|
||||||
'textDocument/semanticTokens/full': [
|
'textDocument/semanticTokens/full': [
|
||||||
LSP.SemanticTokensParams,
|
LSP.SemanticTokensParams,
|
||||||
LSP.SemanticTokens,
|
LSP.SemanticTokens,
|
||||||
@ -23,6 +24,23 @@ interface LSPRequestMap {
|
|||||||
LSP.TextEdit[] | null,
|
LSP.TextEdit[] | null,
|
||||||
]
|
]
|
||||||
'textDocument/foldingRange': [LSP.FoldingRangeParams, LSP.FoldingRange[]]
|
'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
|
// Client to server
|
||||||
@ -124,7 +142,7 @@ export class LanguageServerClient {
|
|||||||
async textDocumentSemanticTokensFull(params: LSP.SemanticTokensParams) {
|
async textDocumentSemanticTokensFull(params: LSP.SemanticTokensParams) {
|
||||||
const serverCapabilities = this.getServerCapabilities()
|
const serverCapabilities = this.getServerCapabilities()
|
||||||
if (!serverCapabilities.semanticTokensProvider) {
|
if (!serverCapabilities.semanticTokensProvider) {
|
||||||
return
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.request('textDocument/semanticTokens/full', params)
|
return this.request('textDocument/semanticTokens/full', params)
|
||||||
@ -133,7 +151,7 @@ export class LanguageServerClient {
|
|||||||
async textDocumentHover(params: LSP.HoverParams) {
|
async textDocumentHover(params: LSP.HoverParams) {
|
||||||
const serverCapabilities = this.getServerCapabilities()
|
const serverCapabilities = this.getServerCapabilities()
|
||||||
if (!serverCapabilities.hoverProvider) {
|
if (!serverCapabilities.hoverProvider) {
|
||||||
return
|
return null
|
||||||
}
|
}
|
||||||
return await this.request('textDocument/hover', params)
|
return await this.request('textDocument/hover', params)
|
||||||
}
|
}
|
||||||
@ -141,7 +159,7 @@ export class LanguageServerClient {
|
|||||||
async textDocumentFormatting(params: LSP.DocumentFormattingParams) {
|
async textDocumentFormatting(params: LSP.DocumentFormattingParams) {
|
||||||
const serverCapabilities = this.getServerCapabilities()
|
const serverCapabilities = this.getServerCapabilities()
|
||||||
if (!serverCapabilities.documentFormattingProvider) {
|
if (!serverCapabilities.documentFormattingProvider) {
|
||||||
return
|
return null
|
||||||
}
|
}
|
||||||
return await this.request('textDocument/formatting', params)
|
return await this.request('textDocument/formatting', params)
|
||||||
}
|
}
|
||||||
@ -149,7 +167,7 @@ export class LanguageServerClient {
|
|||||||
async textDocumentFoldingRange(params: LSP.FoldingRangeParams) {
|
async textDocumentFoldingRange(params: LSP.FoldingRangeParams) {
|
||||||
const serverCapabilities = this.getServerCapabilities()
|
const serverCapabilities = this.getServerCapabilities()
|
||||||
if (!serverCapabilities.foldingRangeProvider) {
|
if (!serverCapabilities.foldingRangeProvider) {
|
||||||
return
|
return null
|
||||||
}
|
}
|
||||||
return await this.request('textDocument/foldingRange', params)
|
return await this.request('textDocument/foldingRange', params)
|
||||||
}
|
}
|
||||||
@ -157,10 +175,58 @@ export class LanguageServerClient {
|
|||||||
async textDocumentCompletion(params: LSP.CompletionParams) {
|
async textDocumentCompletion(params: LSP.CompletionParams) {
|
||||||
const serverCapabilities = this.getServerCapabilities()
|
const serverCapabilities = this.getServerCapabilities()
|
||||||
if (!serverCapabilities.completionProvider) {
|
if (!serverCapabilities.completionProvider) {
|
||||||
return
|
return null
|
||||||
}
|
}
|
||||||
const response = await this.request('textDocument/completion', params)
|
return await this.request('textDocument/completion', params)
|
||||||
return response
|
}
|
||||||
|
|
||||||
|
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) {
|
attachPlugin(plugin: LanguageServerPlugin) {
|
||||||
|
@ -18,7 +18,9 @@ export { Codec } from './client/codec/utils'
|
|||||||
export {
|
export {
|
||||||
lspDiagnosticsEvent,
|
lspDiagnosticsEvent,
|
||||||
lspFormatCodeEvent,
|
lspFormatCodeEvent,
|
||||||
|
lspRenameEvent,
|
||||||
lspSemanticTokensEvent,
|
lspSemanticTokensEvent,
|
||||||
|
lspCodeActionEvent,
|
||||||
} from './plugin/annotation'
|
} from './plugin/annotation'
|
||||||
export {
|
export {
|
||||||
LanguageServerPlugin,
|
LanguageServerPlugin,
|
||||||
|
@ -4,9 +4,13 @@ export enum LspAnnotation {
|
|||||||
SemanticTokens = 'semantic-tokens',
|
SemanticTokens = 'semantic-tokens',
|
||||||
FormatCode = 'format-code',
|
FormatCode = 'format-code',
|
||||||
Diagnostics = 'diagnostics',
|
Diagnostics = 'diagnostics',
|
||||||
|
Rename = 'rename',
|
||||||
|
CodeAction = 'code-action',
|
||||||
}
|
}
|
||||||
|
|
||||||
const lspEvent = Annotation.define<LspAnnotation>()
|
const lspEvent = Annotation.define<LspAnnotation>()
|
||||||
export const lspSemanticTokensEvent = lspEvent.of(LspAnnotation.SemanticTokens)
|
export const lspSemanticTokensEvent = lspEvent.of(LspAnnotation.SemanticTokens)
|
||||||
export const lspFormatCodeEvent = lspEvent.of(LspAnnotation.FormatCode)
|
export const lspFormatCodeEvent = lspEvent.of(LspAnnotation.FormatCode)
|
||||||
export const lspDiagnosticsEvent = lspEvent.of(LspAnnotation.Diagnostics)
|
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,
|
prevSnippetField,
|
||||||
startCompletion,
|
startCompletion,
|
||||||
} from '@codemirror/autocomplete'
|
} from '@codemirror/autocomplete'
|
||||||
|
import type { CompletionContext } from '@codemirror/autocomplete'
|
||||||
import { syntaxTree } from '@codemirror/language'
|
import { syntaxTree } from '@codemirror/language'
|
||||||
import type { Extension } from '@codemirror/state'
|
import type { Extension } from '@codemirror/state'
|
||||||
import { Prec } from '@codemirror/state'
|
import { Prec } from '@codemirror/state'
|
||||||
@ -66,7 +67,7 @@ export default function lspAutocompleteExt(
|
|||||||
defaultKeymap: false,
|
defaultKeymap: false,
|
||||||
override: [
|
override: [
|
||||||
async (context) => {
|
async (context) => {
|
||||||
const { state, pos, explicit, view } = context
|
const { state, pos, view } = context
|
||||||
let value = view?.plugin(plugin)
|
let value = view?.plugin(plugin)
|
||||||
if (!value) return null
|
if (!value) return null
|
||||||
|
|
||||||
@ -77,37 +78,55 @@ export default function lspAutocompleteExt(
|
|||||||
)
|
)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
const line = state.doc.lineAt(pos)
|
const cmpTriggers = getCompletionTriggerKind(
|
||||||
let trigKind: CompletionTriggerKind = CompletionTriggerKind.Invoked
|
context,
|
||||||
let trigChar: string | undefined
|
value.client.getServerCapabilities().completionProvider
|
||||||
if (
|
?.triggerCharacters ?? []
|
||||||
!explicit &&
|
|
||||||
value.client
|
|
||||||
.getServerCapabilities()
|
|
||||||
.completionProvider?.triggerCharacters?.includes(
|
|
||||||
line.text[pos - line.from - 1]
|
|
||||||
)
|
)
|
||||||
) {
|
if (!cmpTriggers) return null
|
||||||
trigKind = CompletionTriggerKind.TriggerCharacter
|
|
||||||
trigChar = line.text[pos - line.from - 1]
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
trigKind === CompletionTriggerKind.Invoked &&
|
|
||||||
!context.matchBefore(/\w+$/)
|
|
||||||
) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return await value.requestCompletion(
|
return await value.requestCompletion(
|
||||||
context,
|
context,
|
||||||
offsetToPos(state.doc, pos),
|
offsetToPos(state.doc, pos),
|
||||||
{
|
cmpTriggers
|
||||||
triggerKind: trigKind,
|
|
||||||
triggerCharacter: trigChar,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
CompletionResult,
|
||||||
} from '@codemirror/autocomplete'
|
} from '@codemirror/autocomplete'
|
||||||
import { completeFromList, snippetCompletion } from '@codemirror/autocomplete'
|
import { completeFromList, snippetCompletion } from '@codemirror/autocomplete'
|
||||||
|
import type { Action, Diagnostic } from '@codemirror/lint'
|
||||||
import { linter } from '@codemirror/lint'
|
import { linter } from '@codemirror/lint'
|
||||||
import type { Extension, StateEffect } from '@codemirror/state'
|
import type { Extension, StateEffect } from '@codemirror/state'
|
||||||
import { Facet, Transaction } 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 { URI } from 'vscode-uri'
|
||||||
|
|
||||||
import type { LanguageServerClient } from '../client'
|
import type { LanguageServerClient } from '../client'
|
||||||
import { lspFormatCodeEvent, lspSemanticTokensEvent } from './annotation'
|
import {
|
||||||
|
lspFormatCodeEvent,
|
||||||
|
lspSemanticTokensEvent,
|
||||||
|
lspRenameEvent,
|
||||||
|
lspCodeActionEvent,
|
||||||
|
} from './annotation'
|
||||||
import lspAutocompleteExt, { CompletionItemKindMap } from './autocomplete'
|
import lspAutocompleteExt, { CompletionItemKindMap } from './autocomplete'
|
||||||
import lspFormatExt from './format'
|
import lspFormatExt from './format'
|
||||||
import lspHoverExt from './hover'
|
import lspHoverExt from './hover'
|
||||||
import lspIndentExt from './indent'
|
import lspIndentExt from './indent'
|
||||||
import type { SemanticToken } from './semantic-tokens'
|
import type { SemanticToken } from './semantic-tokens'
|
||||||
import lspSemanticTokensExt, { addToken } 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, '')
|
const useLast = (values: readonly any[]) => values.reduce((_, v) => v, '')
|
||||||
export const docPathFacet = Facet.define<string, string>({
|
export const docPathFacet = Facet.define<string, string>({
|
||||||
@ -43,6 +59,25 @@ export const workspaceFolders = Facet.define<
|
|||||||
LSP.WorkspaceFolder[]
|
LSP.WorkspaceFolder[]
|
||||||
>({ combine: useLast })
|
>({ 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 {
|
export interface LanguageServerOptions {
|
||||||
// We assume this is the main project directory, we are currently working in.
|
// We assume this is the main project directory, we are currently working in.
|
||||||
workspaceFolders: LSP.WorkspaceFolder[]
|
workspaceFolders: LSP.WorkspaceFolder[]
|
||||||
@ -58,6 +93,9 @@ export interface LanguageServerOptions {
|
|||||||
|
|
||||||
doSemanticTokens?: boolean
|
doSemanticTokens?: boolean
|
||||||
doFoldingRanges?: boolean
|
doFoldingRanges?: boolean
|
||||||
|
|
||||||
|
/** Callback triggered when a go-to-definition action is performed */
|
||||||
|
onGoToDefinition?: (result: DefinitionResult) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LanguageServerPlugin implements PluginValue {
|
export class LanguageServerPlugin implements PluginValue {
|
||||||
@ -82,6 +120,8 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
// document.
|
// document.
|
||||||
private sendScheduled: number | null = null
|
private sendScheduled: number | null = null
|
||||||
|
|
||||||
|
private onGoToDefinition: ((result: DefinitionResult) => void) | undefined
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
options: LanguageServerOptions,
|
options: LanguageServerOptions,
|
||||||
private view: EditorView
|
private view: EditorView
|
||||||
@ -104,6 +144,8 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
|
|
||||||
this.processLspNotification = options.processLspNotification
|
this.processLspNotification = options.processLspNotification
|
||||||
|
|
||||||
|
this.onGoToDefinition = options.onGoToDefinition
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
this.initialize({
|
this.initialize({
|
||||||
documentText: this.getDocText(),
|
documentText: this.getDocText(),
|
||||||
@ -185,8 +227,8 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
dom.classList.add('documentation')
|
dom.classList.add('documentation')
|
||||||
dom.classList.add('hover-tooltip')
|
dom.classList.add('hover-tooltip')
|
||||||
dom.style.zIndex = '99999999'
|
dom.style.zIndex = '99999999'
|
||||||
if (this.allowHTMLContent) dom.innerHTML = formatMarkdownContents(contents)
|
if (this.allowHTMLContent) dom.innerHTML = formatContents(contents)
|
||||||
else dom.textContent = formatMarkdownContents(contents)
|
else dom.textContent = formatContents(contents)
|
||||||
return { pos, end, create: (view) => ({ dom }), above: true }
|
return { pos, end, create: (view) => ({ dom }), above: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,7 +359,7 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
triggerCharacter,
|
triggerCharacter,
|
||||||
}: {
|
}: {
|
||||||
triggerKind: CompletionTriggerKind
|
triggerKind: CompletionTriggerKind
|
||||||
triggerCharacter: string | undefined
|
triggerCharacter?: string
|
||||||
}
|
}
|
||||||
): Promise<CompletionResult | null> {
|
): Promise<CompletionResult | null> {
|
||||||
if (
|
if (
|
||||||
@ -379,8 +421,7 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
const deprecatedHtml = deprecated
|
const deprecatedHtml = deprecated
|
||||||
? '<p><strong>Deprecated</strong></p>'
|
? '<p><strong>Deprecated</strong></p>'
|
||||||
: ''
|
: ''
|
||||||
const htmlString =
|
const htmlString = deprecatedHtml + formatContents(documentation)
|
||||||
deprecatedHtml + formatMarkdownContents(documentation)
|
|
||||||
const htmlNode = document.createElement('div')
|
const htmlNode = document.createElement('div')
|
||||||
htmlNode.style.display = 'contents'
|
htmlNode.style.display = 'contents'
|
||||||
htmlNode.innerHTML = htmlString
|
htmlNode.innerHTML = htmlString
|
||||||
@ -406,6 +447,660 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
return completeFromList(options)(context)
|
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[]) {
|
parseSemanticTokens(view: EditorView, data: number[]) {
|
||||||
// decode the lsp semantic token types
|
// decode the lsp semantic token types
|
||||||
const tokens = []
|
const tokens = []
|
||||||
@ -509,7 +1204,7 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
params
|
params
|
||||||
)
|
)
|
||||||
// this is sometimes slower than our actual typing.
|
// this is sometimes slower than our actual typing.
|
||||||
this.processDiagnostics(params)
|
await this.processDiagnostics(params)
|
||||||
break
|
break
|
||||||
case 'window/logMessage':
|
case 'window/logMessage':
|
||||||
console.log(
|
console.log(
|
||||||
@ -534,25 +1229,74 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
this.processLspNotification?.(this, notification)
|
this.processLspNotification?.(this, notification)
|
||||||
}
|
}
|
||||||
|
|
||||||
processDiagnostics(params: PublishDiagnosticsParams) {
|
async processDiagnostics(params: PublishDiagnosticsParams) {
|
||||||
if (params.uri !== this.getDocUri()) return
|
if (params.uri !== this.getDocUri()) return
|
||||||
|
|
||||||
// Commented to avoid the lint. See TODO below.
|
// Commented to avoid the lint. See TODO below.
|
||||||
// const diagnostics =
|
const rawDiagnostics = params.diagnostics.map(
|
||||||
params.diagnostics
|
async ({ range, message, severity, code }) => {
|
||||||
.map(({ range, message, severity }) => ({
|
const actions = await this.requestCodeActions(range, [code as string])
|
||||||
from: posToOffset(this.view.state.doc, range.start)!,
|
|
||||||
to: posToOffset(this.view.state.doc, range.end)!,
|
const codemirrorActions = actions?.map(
|
||||||
severity: (
|
(action): Action => ({
|
||||||
{
|
name:
|
||||||
[DiagnosticSeverity.Error]: 'error',
|
'command' in action && typeof action.command === 'object'
|
||||||
[DiagnosticSeverity.Warning]: 'warning',
|
? action.command?.title || action.title
|
||||||
[DiagnosticSeverity.Information]: 'info',
|
: action.title,
|
||||||
[DiagnosticSeverity.Hint]: 'info',
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
} as const
|
apply: async () => {
|
||||||
)[severity!],
|
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,
|
message,
|
||||||
}))
|
}
|
||||||
|
|
||||||
|
return diagnostic
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const diagnostics = (await Promise.all(rawDiagnostics))
|
||||||
.filter(
|
.filter(
|
||||||
({ from, to }) =>
|
({ from, to }) =>
|
||||||
from !== null && to !== null && from !== undefined && to !== undefined
|
from !== null && to !== null && from !== undefined && to !== undefined
|
||||||
@ -581,12 +1325,15 @@ export class LanguageServerPluginSpec
|
|||||||
{
|
{
|
||||||
provide(plugin: ViewPlugin<LanguageServerPlugin>): Extension {
|
provide(plugin: ViewPlugin<LanguageServerPlugin>): Extension {
|
||||||
return [
|
return [
|
||||||
|
linter(null),
|
||||||
lspAutocompleteExt(plugin),
|
lspAutocompleteExt(plugin),
|
||||||
lspFormatExt(plugin),
|
lspFormatExt(plugin),
|
||||||
|
lspGoToDefinitionExt(plugin),
|
||||||
lspHoverExt(plugin),
|
lspHoverExt(plugin),
|
||||||
lspIndentExt(),
|
lspIndentExt(),
|
||||||
|
lspRenameExt(plugin),
|
||||||
lspSemanticTokensExt(),
|
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 type { MarkedOptions } from '@ts-stack/markdown'
|
||||||
import { Marked } from '@ts-stack/markdown'
|
import { Marked } from '@ts-stack/markdown'
|
||||||
import type * as LSP from 'vscode-languageserver-protocol'
|
import type * as LSP from 'vscode-languageserver-protocol'
|
||||||
|
import type { EditorView } from '@codemirror/view'
|
||||||
|
|
||||||
import { isArray } from '../lib/utils'
|
import { isArray } from '../lib/utils'
|
||||||
|
|
||||||
@ -36,6 +37,13 @@ export function posToOffset(
|
|||||||
return offset
|
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) {
|
export function offsetToPos(doc: Text, offset: number) {
|
||||||
const line = doc.lineAt(offset)
|
const line = doc.lineAt(offset)
|
||||||
return {
|
return {
|
||||||
@ -48,14 +56,84 @@ const markedOptions: MarkedOptions = {
|
|||||||
gfm: true,
|
gfm: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatMarkdownContents(
|
export function isLSPTextEdit(
|
||||||
contents: LSP.MarkupContent | LSP.MarkedString | LSP.MarkedString[]
|
textEdit?: LSP.TextEdit | LSP.InsertReplaceEdit
|
||||||
): string {
|
): textEdit is LSP.TextEdit {
|
||||||
if (isArray(contents)) {
|
return (textEdit as LSP.TextEdit)?.range !== undefined
|
||||||
return contents.map((c) => formatMarkdownContents(c) + '\n\n').join('')
|
}
|
||||||
} else if (typeof contents === 'string') {
|
|
||||||
return Marked.parse(contents, markedOptions)
|
export function isLSPMarkupContent(
|
||||||
} else {
|
contents: LSP.MarkupContent | LSP.MarkedString | LSP.MarkedString[]
|
||||||
return Marked.parse(contents.value, markedOptions)
|
): 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);
|
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's iterate over the AST and find the node that contains the cursor.
|
||||||
let Some(ast) = self.ast_map.get(&filename) else {
|
let Some(ast) = self.ast_map.get(&filename) else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@ -1419,7 +1482,7 @@ impl LanguageServer for Backend {
|
|||||||
ast.rename_symbol(¶ms.new_name, pos);
|
ast.rename_symbol(¶ms.new_name, pos);
|
||||||
// Now recast it.
|
// Now recast it.
|
||||||
let recast = ast.recast(&Default::default(), 0);
|
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);
|
let range = source_range.to_lsp_range(current_code);
|
||||||
Ok(Some(WorkspaceEdit {
|
Ok(Some(WorkspaceEdit {
|
||||||
changes: Some(HashMap::from([(
|
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> {
|
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")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn test_kcl_lsp_semantic_tokens() {
|
async fn test_kcl_lsp_semantic_tokens() {
|
||||||
let server = kcl_lsp_server(false).await.unwrap();
|
let server = kcl_lsp_server(false).await.unwrap();
|
||||||
@ -1808,13 +2150,91 @@ async fn test_kcl_lsp_rename() {
|
|||||||
vec![tower_lsp::lsp_types::TextEdit {
|
vec![tower_lsp::lsp_types::TextEdit {
|
||||||
range: tower_lsp::lsp_types::Range {
|
range: tower_lsp::lsp_types::Range {
|
||||||
start: tower_lsp::lsp_types::Position { line: 0, character: 0 },
|
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()
|
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")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn test_kcl_lsp_diagnostic_no_errors() {
|
async fn test_kcl_lsp_diagnostic_no_errors() {
|
||||||
let server = kcl_lsp_server(false).await.unwrap();
|
let server = kcl_lsp_server(false).await.unwrap();
|
||||||
|
@ -2644,3 +2644,45 @@ mod import_mesh_clone {
|
|||||||
super::execute(TEST_NAME, true).await
|
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,
|
LanguageServerClient,
|
||||||
LanguageServerOptions,
|
LanguageServerOptions,
|
||||||
} from '@kittycad/codemirror-lsp-client'
|
} 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 { updateOutsideEditorEvent } from '@src/editor/manager'
|
||||||
import { codeManagerUpdateEvent } from '@src/lang/codeManager'
|
import { codeManagerUpdateEvent } from '@src/lang/codeManager'
|
||||||
import { codeManager, editorManager, kclManager } from '@src/lib/singletons'
|
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.
|
// We want to ignore other events outside the editor.
|
||||||
isRelevant = false
|
isRelevant = false
|
||||||
break
|
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;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cm-rename-popup {
|
||||||
|
/* we want to overpower anything else */
|
||||||
|
z-index: 99999999999 !important;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes blink {
|
@keyframes blink {
|
||||||
0%,
|
0%,
|
||||||
100% {
|
100% {
|
||||||
@ -220,18 +225,22 @@ code {
|
|||||||
@apply bg-transparent !important;
|
@apply bg-transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#code-mirror-override .cm-tooltip {
|
#code-mirror-override .cm-tooltip,
|
||||||
|
.cm-tooltip {
|
||||||
@apply text-xs shadow-md;
|
@apply text-xs shadow-md;
|
||||||
@apply bg-chalkboard-10 text-chalkboard-80;
|
@apply bg-chalkboard-10 text-chalkboard-80;
|
||||||
@apply rounded-sm border-solid border border-chalkboard-40/30 border-l-liquid-10;
|
@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 bg-chalkboard-110 text-chalkboard-40;
|
||||||
@apply border-chalkboard-70/20 border-l-liquid-70;
|
@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;
|
@apply py-1 px-2 w-max max-w-md;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import type { Diagnostic as CodeMirrorDiagnostic } from '@codemirror/lint'
|
import type { Diagnostic as CodeMirrorDiagnostic } from '@codemirror/lint'
|
||||||
import type { Text } from '@codemirror/state'
|
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 { EditorView } from 'codemirror'
|
||||||
import type { Diagnostic as LspDiagnostic } from 'vscode-languageserver-protocol'
|
import type { Diagnostic as LspDiagnostic } from 'vscode-languageserver-protocol'
|
||||||
|
|
||||||
@ -343,6 +346,7 @@ export function compilationErrorsToDiagnostics(
|
|||||||
to: suggestion.source_range[1],
|
to: suggestion.source_range[1],
|
||||||
insert: suggestion.insert,
|
insert: suggestion.insert,
|
||||||
},
|
},
|
||||||
|
annotations: [lspCodeActionEvent],
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { Diagnostic } from '@codemirror/lint'
|
import type { Diagnostic } from '@codemirror/lint'
|
||||||
|
import { lspCodeActionEvent } from '@kittycad/codemirror-lsp-client'
|
||||||
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
||||||
|
|
||||||
import { KCLError } from '@src/lang/errors'
|
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 { EXECUTE_AST_INTERRUPT_ERROR_STRING } from '@src/lib/constants'
|
||||||
import type RustContext from '@src/lib/rustContext'
|
import type RustContext from '@src/lib/rustContext'
|
||||||
import { jsAppSettings } from '@src/lib/settings/settingsUtils'
|
import { jsAppSettings } from '@src/lib/settings/settingsUtils'
|
||||||
|
import type { EditorView } from 'codemirror'
|
||||||
|
|
||||||
export type ToolTip =
|
export type ToolTip =
|
||||||
| 'lineTo'
|
| 'lineTo'
|
||||||
@ -143,11 +144,31 @@ export async function lintAst({
|
|||||||
try {
|
try {
|
||||||
const discovered_findings = await kclLint(ast)
|
const discovered_findings = await kclLint(ast)
|
||||||
return discovered_findings.map((lint) => {
|
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 {
|
return {
|
||||||
message: lint.finding.title,
|
|
||||||
severity: 'info',
|
|
||||||
from: lint.pos[0],
|
from: lint.pos[0],
|
||||||
to: lint.pos[1],
|
to: lint.pos[1],
|
||||||
|
message: lint.finding.title,
|
||||||
|
severity: 'info',
|
||||||
|
actions,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
|
Reference in New Issue
Block a user