Send telemetry (#1702)
* restart on auth Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix deps Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix Signed-off-by: Jess Frazelle <github@jessfraz.com> * hash the iuser Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * add comment; Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * zip up the contents Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix logic Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * add tests Signed-off-by: Jess Frazelle <github@jessfraz.com> * more code coverage Signed-off-by: Jess Frazelle <github@jessfraz.com> * u[dates Signed-off-by: Jess Frazelle <github@jessfraz.com> * u[dates 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> * fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * up[dates Signed-off-by: Jess Frazelle <github@jessfraz.com> * more tests Signed-off-by: Jess Frazelle <github@jessfraz.com> * more coverage Signed-off-by: Jess Frazelle <github@jessfraz.com> * more coverage Signed-off-by: Jess Frazelle <github@jessfraz.com> * add 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> * updates 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> * updates 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> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * more tests Signed-off-by: Jess Frazelle <github@jessfraz.com> * cleanup dead code Signed-off-by: Jess Frazelle <github@jessfraz.com> * start of accept / reject Signed-off-by: Jess Frazelle <github@jessfraz.com> * accept/reject Signed-off-by: Jess Frazelle <github@jessfraz.com> * cleanup Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
10
.github/workflows/cargo-test.yml
vendored
10
.github/workflows/cargo-test.yml
vendored
@ -62,8 +62,16 @@ jobs:
|
||||
shell: bash
|
||||
run: |-
|
||||
cd "${{ matrix.dir }}"
|
||||
cargo nextest run --workspace --no-fail-fast -P ci 2>&1 | tee /tmp/github-actions.log
|
||||
cargo llvm-cov nextest --all --lcov --output-path lcov.info --test-threads=1 --no-fail-fast -P ci 2>&1 | tee /tmp/github-actions.log
|
||||
env:
|
||||
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
|
||||
RUST_MIN_STACK: 10485760000
|
||||
- name: Upload to codecov.io
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{secrets.CODECOV_TOKEN}}
|
||||
fail_ci_if_error: true
|
||||
flags: wasm-lib
|
||||
verbose: true
|
||||
files: lcov.info
|
||||
|
||||
|
@ -40,9 +40,11 @@ export function App() {
|
||||
const filePath = useAbsoluteFilePath()
|
||||
const { onProjectOpen } = useLspContext()
|
||||
|
||||
const projectName = project?.name || null
|
||||
const projectPath = project?.path || null
|
||||
useEffect(() => {
|
||||
onProjectOpen(project || null, file || null)
|
||||
}, [])
|
||||
onProjectOpen({ name: projectName, path: projectPath }, file || null)
|
||||
}, [projectName, projectPath])
|
||||
|
||||
useHotKeyListener()
|
||||
const {
|
||||
|
@ -14,7 +14,6 @@ import { LanguageSupport } from '@codemirror/language'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { paths } from 'lib/paths'
|
||||
import { FileEntry } from '@tauri-apps/api/fs'
|
||||
import { ProjectWithEntryPointMetadata } from 'lib/types'
|
||||
|
||||
const DEFAULT_FILE_NAME: string = 'main.kcl'
|
||||
|
||||
@ -40,7 +39,7 @@ type LspContext = {
|
||||
redirect: boolean
|
||||
) => void
|
||||
onProjectOpen: (
|
||||
project: ProjectWithEntryPointMetadata | null,
|
||||
project: { name: string | null; path: string | null } | null,
|
||||
file: FileEntry | null
|
||||
) => void
|
||||
onFileOpen: (filePath: string | null, projectPath: string | null) => void
|
||||
@ -69,6 +68,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
}))
|
||||
|
||||
const { auth } = useSettingsAuthContext()
|
||||
const token = auth?.context?.token
|
||||
const navigate = useNavigate()
|
||||
|
||||
// So this is a bit weird, we need to initialize the lsp server and client.
|
||||
@ -80,7 +80,6 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const client = new Client(fromServer, intoServer)
|
||||
if (!TEST) {
|
||||
Server.initialize(intoServer, fromServer).then((lspServer) => {
|
||||
const token = auth?.context?.token
|
||||
lspServer.start('kcl', token)
|
||||
setIsKclLspServerReady(true)
|
||||
})
|
||||
@ -88,7 +87,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
|
||||
const lspClient = new LanguageServerClient({ client, name: 'kcl' })
|
||||
return { lspClient }
|
||||
}, [setIsKclLspServerReady])
|
||||
}, [setIsKclLspServerReady, token])
|
||||
|
||||
// Here we initialize the plugin which will start the client.
|
||||
// Now that we have multi-file support the name of the file is a dep of
|
||||
@ -116,7 +115,6 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const client = new Client(fromServer, intoServer)
|
||||
if (!TEST) {
|
||||
Server.initialize(intoServer, fromServer).then((lspServer) => {
|
||||
const token = auth?.context?.token
|
||||
lspServer.start('copilot', token)
|
||||
setIsCopilotLspServerReady(true)
|
||||
})
|
||||
@ -124,7 +122,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
|
||||
const lspClient = new LanguageServerClient({ client, name: 'copilot' })
|
||||
return { lspClient }
|
||||
}, [setIsCopilotLspServerReady])
|
||||
}, [setIsCopilotLspServerReady, token])
|
||||
|
||||
// Here we initialize the plugin which will start the client.
|
||||
// When we have multi-file support the name of the file will be a dep of
|
||||
@ -172,7 +170,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
}
|
||||
|
||||
const onProjectOpen = (
|
||||
project: ProjectWithEntryPointMetadata | null,
|
||||
project: { name: string | null; path: string | null } | null,
|
||||
file: FileEntry | null
|
||||
) => {
|
||||
const projectName = project?.name || 'ProjectRoot'
|
||||
|
@ -2,67 +2,10 @@ import type * as LSP from 'vscode-languageserver-protocol'
|
||||
import Client from './client'
|
||||
import { SemanticToken, deserializeTokens } from './kcl/semantic_tokens'
|
||||
import { LanguageServerPlugin } from 'editor/plugins/lsp/plugin'
|
||||
|
||||
export interface CopilotGetCompletionsParams {
|
||||
doc: {
|
||||
source: string
|
||||
tabSize: number
|
||||
indentSize: number
|
||||
insertSpaces: boolean
|
||||
path: string
|
||||
uri: string
|
||||
relativePath: string
|
||||
languageId: string
|
||||
position: {
|
||||
line: number
|
||||
character: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface CopilotGetCompletionsResult {
|
||||
completions: {
|
||||
text: string
|
||||
position: {
|
||||
line: number
|
||||
character: number
|
||||
}
|
||||
uuid: string
|
||||
range: {
|
||||
start: {
|
||||
line: number
|
||||
character: number
|
||||
}
|
||||
end: {
|
||||
line: number
|
||||
character: number
|
||||
}
|
||||
}
|
||||
displayText: string
|
||||
point: {
|
||||
line: number
|
||||
character: number
|
||||
}
|
||||
region: {
|
||||
start: {
|
||||
line: number
|
||||
character: number
|
||||
}
|
||||
end: {
|
||||
line: number
|
||||
character: number
|
||||
}
|
||||
}
|
||||
}[]
|
||||
}
|
||||
|
||||
interface CopilotAcceptCompletionParams {
|
||||
uuid: string
|
||||
}
|
||||
|
||||
interface CopilotRejectCompletionParams {
|
||||
uuids: string[]
|
||||
}
|
||||
import { CopilotLspCompletionParams } from 'wasm-lib/kcl/bindings/CopilotLspCompletionParams'
|
||||
import { CopilotCompletionResponse } from 'wasm-lib/kcl/bindings/CopilotCompletionResponse'
|
||||
import { CopilotAcceptCompletionParams } from 'wasm-lib/kcl/bindings/CopilotAcceptCompletionParams'
|
||||
import { CopilotRejectCompletionParams } from 'wasm-lib/kcl/bindings/CopilotRejectCompletionParams'
|
||||
|
||||
// https://microsoft.github.io/language-server-protocol/specifications/specification-current/
|
||||
|
||||
@ -78,7 +21,7 @@ interface LSPRequestMap {
|
||||
LSP.SemanticTokensParams,
|
||||
LSP.SemanticTokens
|
||||
]
|
||||
getCompletions: [CopilotGetCompletionsParams, CopilotGetCompletionsResult]
|
||||
getCompletions: [CopilotLspCompletionParams, CopilotCompletionResponse]
|
||||
notifyAccepted: [CopilotAcceptCompletionParams, any]
|
||||
notifyRejected: [CopilotRejectCompletionParams, any]
|
||||
}
|
||||
@ -271,7 +214,7 @@ export class LanguageServerClient {
|
||||
return this.client.notify(method, params)
|
||||
}
|
||||
|
||||
async getCompletion(params: CopilotGetCompletionsParams) {
|
||||
async getCompletion(params: CopilotLspCompletionParams) {
|
||||
const response = await this.request('getCompletions', params)
|
||||
//
|
||||
this.queuedUids = [...response.completions.map((c) => c.uuid)]
|
||||
|
@ -37,6 +37,7 @@ import { homeCommandBarConfig } from 'lib/commandBarConfigs/homeCommandConfig'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { isTauri } from 'lib/isTauri'
|
||||
import { kclManager } from 'lang/KclSingleton'
|
||||
import { useLspContext } from 'components/LspProvider'
|
||||
|
||||
// This route only opens in the Tauri desktop context for now,
|
||||
// as defined in Router.tsx, so we can use the Tauri APIs and types.
|
||||
@ -51,6 +52,7 @@ const Home = () => {
|
||||
send: sendToSettings,
|
||||
},
|
||||
} = useSettingsAuthContext()
|
||||
const { onProjectOpen } = useLspContext()
|
||||
|
||||
// Set the default directory if it's been updated
|
||||
// during the loading of the home page. This is wrapped
|
||||
@ -84,12 +86,16 @@ const Home = () => {
|
||||
event: EventFrom<typeof homeMachine>
|
||||
) => {
|
||||
if (event.data && 'name' in event.data) {
|
||||
commandBarSend({ type: 'Close' })
|
||||
navigate(
|
||||
`${paths.FILE}/${encodeURIComponent(
|
||||
context.defaultDirectory + sep + event.data.name
|
||||
)}`
|
||||
let projectPath = context.defaultDirectory + sep + event.data.name
|
||||
onProjectOpen(
|
||||
{
|
||||
name: event.data.name,
|
||||
path: projectPath,
|
||||
},
|
||||
null
|
||||
)
|
||||
commandBarSend({ type: 'Close' })
|
||||
navigate(`${paths.FILE}/${encodeURIComponent(projectPath)}`)
|
||||
}
|
||||
},
|
||||
toastSuccess: (_, event) => toast.success((event.data || '') + ''),
|
||||
|
38
src/wasm-lib/Cargo.lock
generated
38
src/wasm-lib/Cargo.lock
generated
@ -522,9 +522,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.34"
|
||||
version = "0.4.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b"
|
||||
checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
@ -1914,6 +1914,7 @@ dependencies = [
|
||||
"async-recursion",
|
||||
"async-trait",
|
||||
"bson",
|
||||
"chrono",
|
||||
"clap",
|
||||
"criterion",
|
||||
"dashmap",
|
||||
@ -1929,6 +1930,7 @@ dependencies = [
|
||||
"kittycad-execution-plan-macros",
|
||||
"kittycad-execution-plan-traits",
|
||||
"lazy_static",
|
||||
"mime_guess",
|
||||
"parse-display 0.9.0",
|
||||
"pretty_assertions",
|
||||
"reqwest",
|
||||
@ -1936,6 +1938,7 @@ dependencies = [
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
@ -1946,6 +1949,7 @@ dependencies = [
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"winnow",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1962,9 +1966,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad"
|
||||
version = "0.2.59"
|
||||
version = "0.2.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4080db4364c103601db486e4a8aa889ea56c011991e4c454373d8050a165d3da"
|
||||
checksum = "f8aa5906d0730bd90f6b3331fe57c04951d00743169a29ee96408767b4060605"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1978,6 +1982,7 @@ dependencies = [
|
||||
"http 0.2.9",
|
||||
"itertools 0.10.5",
|
||||
"log",
|
||||
"mime_guess",
|
||||
"parse-display 0.8.2",
|
||||
"phonenumber",
|
||||
"rand 0.8.5",
|
||||
@ -2000,7 +2005,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kittycad-execution-plan"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#c554cbeda3f217c1baab8a33ffad50e2ecdc8ab9"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#c7722adf9744b9e4eead0a7a88662ad5e7e3adbf"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"insta",
|
||||
@ -2020,7 +2025,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kittycad-execution-plan-macros"
|
||||
version = "0.1.8"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#c554cbeda3f217c1baab8a33ffad50e2ecdc8ab9"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#c7722adf9744b9e4eead0a7a88662ad5e7e3adbf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -2030,7 +2035,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kittycad-execution-plan-traits"
|
||||
version = "0.1.12"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#c554cbeda3f217c1baab8a33ffad50e2ecdc8ab9"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#c7722adf9744b9e4eead0a7a88662ad5e7e3adbf"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"thiserror",
|
||||
@ -2040,7 +2045,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kittycad-modeling-cmds"
|
||||
version = "0.1.28"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#c554cbeda3f217c1baab8a33ffad50e2ecdc8ab9"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#c7722adf9744b9e4eead0a7a88662ad5e7e3adbf"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@ -2068,7 +2073,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "kittycad-modeling-cmds-macros"
|
||||
version = "0.1.2"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#c554cbeda3f217c1baab8a33ffad50e2ecdc8ab9"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#c7722adf9744b9e4eead0a7a88662ad5e7e3adbf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -2077,8 +2082,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad-modeling-session"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#c554cbeda3f217c1baab8a33ffad50e2ecdc8ab9"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#c7722adf9744b9e4eead0a7a88662ad5e7e3adbf"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"kittycad",
|
||||
@ -5505,6 +5510,17 @@ dependencies = [
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "0.6.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"crc32fast",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zune-inflate"
|
||||
version = "0.2.54"
|
||||
|
@ -59,7 +59,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
kittycad = { version = "0.2.59", default-features = false, features = ["js", "requests"] }
|
||||
kittycad = { version = "0.2.60", default-features = false, features = ["js", "requests"] }
|
||||
kittycad-execution-plan = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
|
||||
kittycad-execution-plan-macros = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
|
||||
kittycad-execution-plan-traits = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
|
||||
|
@ -14,6 +14,7 @@ keywords = ["kcl", "KittyCAD", "CAD"]
|
||||
anyhow = { version = "1.0.80", features = ["backtrace"] }
|
||||
async-recursion = "1.0.5"
|
||||
async-trait = "0.1.77"
|
||||
chrono = "0.4.35"
|
||||
clap = { version = "4.5.2", features = ["cargo", "derive", "env", "unicode"], optional = true }
|
||||
dashmap = "5.5.3"
|
||||
databake = { version = "0.1.7", features = ["derive"] }
|
||||
@ -25,16 +26,19 @@ kittycad = { workspace = true }
|
||||
kittycad-execution-plan-macros = { workspace = true }
|
||||
kittycad-execution-plan-traits = { workspace = true }
|
||||
lazy_static = "1.4.0"
|
||||
mime_guess = "2.0.4"
|
||||
parse-display = "0.9.0"
|
||||
reqwest = { version = "0.11.26", default-features = false, features = ["stream", "rustls-tls"] }
|
||||
ropey = "1.6.1"
|
||||
schemars = { version = "0.8.16", features = ["impl_json_schema", "url", "uuid1"] }
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
serde_json = "1.0.114"
|
||||
sha2 = "0.10.8"
|
||||
thiserror = "1.0.57"
|
||||
ts-rs = { version = "7.1.1", features = ["uuid-impl"] }
|
||||
uuid = { version = "1.7.0", features = ["v4", "js", "serde"] }
|
||||
winnow = "0.5.40"
|
||||
zip = { version = "0.6.6", default-features = false }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
js-sys = { version = "0.3.69" }
|
||||
|
@ -75,11 +75,33 @@ pub trait Backend {
|
||||
}
|
||||
|
||||
async fn do_did_change_workspace_folders(&self, params: DidChangeWorkspaceFoldersParams) {
|
||||
// If we are adding a folder that we were previously on, we should not clear the
|
||||
// state.
|
||||
let should_clear = if !params.event.added.is_empty() {
|
||||
let mut should_clear = false;
|
||||
for folder in params.event.added.iter() {
|
||||
if !self
|
||||
.workspace_folders()
|
||||
.iter()
|
||||
.any(|f| f.uri == folder.uri && f.name == folder.name)
|
||||
{
|
||||
should_clear = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
should_clear
|
||||
} else {
|
||||
!(params.event.removed.is_empty() && params.event.added.is_empty())
|
||||
};
|
||||
|
||||
self.add_workspace_folders(params.event.added.clone());
|
||||
self.remove_workspace_folders(params.event.removed);
|
||||
// Remove the code from the current code map.
|
||||
// We do this since it means the user is changing projects so let's refresh the state.
|
||||
self.clear_code_state();
|
||||
if !self.current_code_map().is_empty() && should_clear {
|
||||
self.clear_code_state();
|
||||
}
|
||||
for added in params.event.added {
|
||||
// Try to read all the files in the project.
|
||||
let project_dir = added.uri.to_string().replace("file://", "");
|
||||
@ -181,14 +203,5 @@ pub trait Backend {
|
||||
self.client()
|
||||
.log_message(MessageType::INFO, format!("document closed: {:?}", params))
|
||||
.await;
|
||||
self.client()
|
||||
.log_message(MessageType::INFO, format!("uri: {:?}", params.text_document.uri))
|
||||
.await;
|
||||
// Get the workspace folders.
|
||||
// The key of the workspace folder is the project name.
|
||||
let workspace_folders = self.workspace_folders();
|
||||
self.client()
|
||||
.log_message(MessageType::INFO, format!("workspace: {:?}", workspace_folders))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,8 @@ use crate::lsp::{
|
||||
copilot::types::{CopilotCompletionResponse, CopilotEditorInfo, CopilotLspCompletionParams, DocParams},
|
||||
};
|
||||
|
||||
use self::types::{CopilotAcceptCompletionParams, CopilotCompletionTelemetry, CopilotRejectCompletionParams};
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub struct Success {
|
||||
success: bool,
|
||||
@ -39,7 +41,7 @@ impl Success {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Backend {
|
||||
/// The client is used to send notifications and requests to the client.
|
||||
pub client: tower_lsp::Client,
|
||||
@ -54,7 +56,9 @@ pub struct Backend {
|
||||
/// The editor info is used to store information about the editor.
|
||||
pub editor_info: Arc<RwLock<CopilotEditorInfo>>,
|
||||
/// The cache is used to store the results of previous requests.
|
||||
pub cache: cache::CopilotCache,
|
||||
pub cache: Arc<cache::CopilotCache>,
|
||||
/// Storage so we can send telemetry data back out.
|
||||
pub telemetry: DashMap<uuid::Uuid, CopilotCompletionTelemetry>,
|
||||
}
|
||||
|
||||
// Implement the shared backend trait for the language server.
|
||||
@ -158,7 +162,7 @@ impl Backend {
|
||||
let pos = params.doc.position;
|
||||
let uri = params.doc.uri.to_string();
|
||||
let rope = ropey::Rope::from_str(¶ms.doc.source);
|
||||
let offset = crate::lsp::util::position_to_offset(pos, &rope).unwrap_or_default();
|
||||
let offset = crate::lsp::util::position_to_offset(pos.into(), &rope).unwrap_or_default();
|
||||
|
||||
Ok(DocParams {
|
||||
uri: uri.to_string(),
|
||||
@ -166,7 +170,7 @@ impl Backend {
|
||||
language: params.doc.language_id.to_string(),
|
||||
prefix: crate::lsp::util::get_text_before(offset, &rope).unwrap_or_default(),
|
||||
suffix: crate::lsp::util::get_text_after(offset, &rope).unwrap_or_default(),
|
||||
line_before: crate::lsp::util::get_line_before(pos, &rope).unwrap_or_default(),
|
||||
line_before: crate::lsp::util::get_line_before(pos.into(), &rope).unwrap_or_default(),
|
||||
rope,
|
||||
})
|
||||
}
|
||||
@ -185,37 +189,69 @@ impl Backend {
|
||||
let line_before = doc_params.line_before.to_string();
|
||||
|
||||
// Let's not call it yet since it's not our model.
|
||||
/*let completion_list = self
|
||||
.get_completions(doc_params.language, doc_params.prefix, doc_params.suffix)
|
||||
.await
|
||||
.map_err(|err| Error {
|
||||
code: tower_lsp::jsonrpc::ErrorCode::from(69),
|
||||
data: None,
|
||||
message: Cow::from(format!("Failed to get completions: {}", err)),
|
||||
})?;*/
|
||||
// We will need to wrap in spawn_local like we do in kcl/mod.rs for wasm only.
|
||||
#[cfg(test)]
|
||||
let completion_list = self
|
||||
.get_completions(doc_params.language, doc_params.prefix, doc_params.suffix)
|
||||
.await
|
||||
.map_err(|err| Error {
|
||||
code: tower_lsp::jsonrpc::ErrorCode::from(69),
|
||||
data: None,
|
||||
message: Cow::from(format!("Failed to get completions: {}", err)),
|
||||
})?;
|
||||
#[cfg(not(test))]
|
||||
let completion_list = vec![];
|
||||
|
||||
let response = CopilotCompletionResponse::from_str_vec(completion_list, line_before, doc_params.pos);
|
||||
// Set the telemetry data for each completion.
|
||||
for completion in response.completions.iter() {
|
||||
let telemetry = CopilotCompletionTelemetry {
|
||||
completion: completion.clone(),
|
||||
params: params.clone(),
|
||||
};
|
||||
self.telemetry.insert(completion.uuid, telemetry);
|
||||
}
|
||||
self.cache
|
||||
.set_cached_result(&doc_params.uri, &doc_params.pos.line, &response);
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub async fn accept_completions(&self, params: Vec<String>) {
|
||||
pub async fn accept_completion(&self, params: CopilotAcceptCompletionParams) {
|
||||
self.client
|
||||
.log_message(MessageType::INFO, format!("Accepted completions: {:?}", params))
|
||||
.await;
|
||||
|
||||
// TODO: send telemetry data back out that we accepted the completions
|
||||
// Get the original telemetry data.
|
||||
let Some((_, original)) = self.telemetry.remove(¶ms.uuid) else {
|
||||
return;
|
||||
};
|
||||
|
||||
self.client
|
||||
.log_message(MessageType::INFO, format!("Original telemetry: {:?}", original))
|
||||
.await;
|
||||
|
||||
// TODO: Send the telemetry data to the zoo api.
|
||||
}
|
||||
|
||||
pub async fn reject_completions(&self, params: Vec<String>) {
|
||||
pub async fn reject_completions(&self, params: CopilotRejectCompletionParams) {
|
||||
self.client
|
||||
.log_message(MessageType::INFO, format!("Rejected completions: {:?}", params))
|
||||
.await;
|
||||
|
||||
// TODO: send telemetry data back out that we rejected the completions
|
||||
// Get the original telemetry data.
|
||||
let mut originals: Vec<CopilotCompletionTelemetry> = Default::default();
|
||||
for uuid in params.uuids {
|
||||
if let Some((_, original)) = self.telemetry.remove(&uuid) {
|
||||
originals.push(original);
|
||||
}
|
||||
}
|
||||
|
||||
self.client
|
||||
.log_message(MessageType::INFO, format!("Original telemetry: {:?}", originals))
|
||||
.await;
|
||||
|
||||
// TODO: Send the telemetry data to the zoo api.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,18 +2,58 @@
|
||||
|
||||
use ropey::Rope;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tower_lsp::lsp_types::{Position, Range};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
/// Position in a text document expressed as zero-based line and character offset.
|
||||
/// A position is between two characters like an 'insert' cursor in a editor.
|
||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Default, Deserialize, Serialize, ts_rs::TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CopilotCyclingCompletion {
|
||||
pub display_text: String, // partial text
|
||||
pub text: String, // fulltext
|
||||
pub range: Range, // start char always 0
|
||||
pub position: Position,
|
||||
#[ts(export)]
|
||||
pub struct CopilotPosition {
|
||||
/// Line position in a document (zero-based).
|
||||
pub line: u32,
|
||||
/// Character offset on a line in a document (zero-based). The meaning of this
|
||||
/// offset is determined by the negotiated `PositionEncodingKind`.
|
||||
///
|
||||
/// If the character value is greater than the line length it defaults back
|
||||
/// to the line length.
|
||||
pub character: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
impl From<CopilotPosition> for tower_lsp::lsp_types::Position {
|
||||
fn from(position: CopilotPosition) -> Self {
|
||||
tower_lsp::lsp_types::Position {
|
||||
line: position.line,
|
||||
character: position.character,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A range in a text document expressed as (zero-based) start and end positions.
|
||||
/// A range is comparable to a selection in an editor. Therefore the end position is exclusive.
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone, Default, Deserialize, Serialize, ts_rs::TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct CopilotRange {
|
||||
/// The range's start position.
|
||||
pub start: CopilotPosition,
|
||||
/// The range's end position.
|
||||
pub end: CopilotPosition,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, ts_rs::TS, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct CopilotCyclingCompletion {
|
||||
pub uuid: uuid::Uuid, // unique id we use for tracking accepted or rejected completions
|
||||
pub display_text: String, // partial text
|
||||
pub text: String, // fulltext
|
||||
pub range: CopilotRange, // start char always 0
|
||||
pub position: CopilotPosition,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, ts_rs::TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct Choices {
|
||||
pub text: String,
|
||||
pub index: i16,
|
||||
@ -21,14 +61,16 @@ pub struct Choices {
|
||||
pub logprobs: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, ts_rs::TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct CopilotCompletionResponse {
|
||||
pub completions: Vec<CopilotCyclingCompletion>,
|
||||
pub cancellation_reason: Option<String>,
|
||||
}
|
||||
|
||||
impl CopilotCompletionResponse {
|
||||
pub fn from_str_vec(str_vec: Vec<String>, line_before: String, pos: Position) -> Self {
|
||||
pub fn from_str_vec(str_vec: Vec<String>, line_before: String, pos: CopilotPosition) -> Self {
|
||||
let completions = str_vec
|
||||
.iter()
|
||||
.map(|x| CopilotCyclingCompletion::new(x.to_string(), line_before.to_string(), pos))
|
||||
@ -41,19 +83,20 @@ impl CopilotCompletionResponse {
|
||||
}
|
||||
|
||||
impl CopilotCyclingCompletion {
|
||||
pub fn new(text: String, line_before: String, position: Position) -> Self {
|
||||
pub fn new(text: String, line_before: String, position: CopilotPosition) -> Self {
|
||||
let display_text = text.clone();
|
||||
let text = format!("{}{}", line_before, text);
|
||||
let end_char = text.find('\n').unwrap_or(text.len()) as u32;
|
||||
Self {
|
||||
uuid: uuid::Uuid::new_v4(),
|
||||
display_text, // partial text
|
||||
text, // fulltext
|
||||
range: Range {
|
||||
start: Position {
|
||||
range: CopilotRange {
|
||||
start: CopilotPosition {
|
||||
character: 0,
|
||||
line: position.line,
|
||||
},
|
||||
end: Position {
|
||||
end: CopilotPosition {
|
||||
character: end_char,
|
||||
line: position.line,
|
||||
},
|
||||
@ -63,17 +106,19 @@ impl CopilotCyclingCompletion {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, ts_rs::TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct LanguageEntry {
|
||||
language_id: String,
|
||||
#[ts(export)]
|
||||
pub struct LanguageEntry {
|
||||
pub language_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, ts_rs::TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct EditorConfiguration {
|
||||
disabled_languages: Vec<LanguageEntry>,
|
||||
enable_auto_completions: bool,
|
||||
#[ts(export)]
|
||||
pub struct EditorConfiguration {
|
||||
pub disabled_languages: Vec<LanguageEntry>,
|
||||
pub enable_auto_completions: bool,
|
||||
}
|
||||
|
||||
impl Default for EditorConfiguration {
|
||||
@ -84,47 +129,77 @@ impl Default for EditorConfiguration {
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone, ts_rs::TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct EditorInfo {
|
||||
name: String,
|
||||
version: String,
|
||||
#[ts(export)]
|
||||
pub struct EditorInfo {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone, ts_rs::TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct CopilotEditorInfo {
|
||||
editor_configuration: EditorConfiguration,
|
||||
editor_info: EditorInfo,
|
||||
editor_plugin_info: EditorInfo,
|
||||
pub editor_configuration: EditorConfiguration,
|
||||
pub editor_info: EditorInfo,
|
||||
pub editor_plugin_info: EditorInfo,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone, ts_rs::TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct DocParams {
|
||||
#[serde(skip)]
|
||||
pub rope: Rope,
|
||||
pub uri: String,
|
||||
pub pos: Position,
|
||||
pub pos: CopilotPosition,
|
||||
pub language: String,
|
||||
pub line_before: String,
|
||||
pub prefix: String,
|
||||
pub suffix: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone, ts_rs::TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct CopilotLspCompletionParams {
|
||||
pub doc: CopilotDocParams,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone, ts_rs::TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct CopilotDocParams {
|
||||
pub indent_size: u32,
|
||||
pub insert_spaces: bool,
|
||||
pub language_id: String,
|
||||
pub path: String,
|
||||
pub position: Position,
|
||||
pub position: CopilotPosition,
|
||||
pub relative_path: String,
|
||||
pub source: String,
|
||||
pub tab_size: u32,
|
||||
pub uri: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone, ts_rs::TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct CopilotAcceptCompletionParams {
|
||||
pub uuid: uuid::Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone, ts_rs::TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct CopilotRejectCompletionParams {
|
||||
pub uuids: Vec<uuid::Uuid>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, ts_rs::TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct CopilotCompletionTelemetry {
|
||||
pub completion: CopilotCyclingCompletion,
|
||||
pub params: CopilotLspCompletionParams,
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
//! Functions for the `kcl` lsp server.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, io::Write, str::FromStr};
|
||||
|
||||
use anyhow::Result;
|
||||
#[cfg(feature = "cli")]
|
||||
use clap::Parser;
|
||||
use dashmap::DashMap;
|
||||
use sha2::Digest;
|
||||
use tower_lsp::{
|
||||
jsonrpc::Result as RpcResult,
|
||||
lsp_types::{
|
||||
@ -44,6 +45,7 @@ pub struct Server {
|
||||
}
|
||||
|
||||
/// The lsp server backend.
|
||||
#[derive(Clone)]
|
||||
pub struct Backend {
|
||||
/// The client for the backend.
|
||||
pub client: Client,
|
||||
@ -285,6 +287,87 @@ impl Backend {
|
||||
|
||||
completions
|
||||
}
|
||||
|
||||
pub fn create_zip(&self) -> Result<Vec<u8>> {
|
||||
// Collect all the file data we know.
|
||||
let mut buf = vec![];
|
||||
let mut zip = zip::ZipWriter::new(std::io::Cursor::new(&mut buf));
|
||||
for entry in self.current_code_map.iter() {
|
||||
let file_name = entry.key().replace("file://", "").to_string();
|
||||
|
||||
let options = zip::write::FileOptions::default().compression_method(zip::CompressionMethod::Stored);
|
||||
zip.start_file(file_name, options)?;
|
||||
zip.write_all(entry.value())?;
|
||||
}
|
||||
// Apply the changes you've made.
|
||||
// Dropping the `ZipWriter` will have the same effect, but may silently fail
|
||||
zip.finish()?;
|
||||
drop(zip);
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
pub async fn send_telemetry(&self) -> Result<()> {
|
||||
// Get information about the user.
|
||||
let user = self
|
||||
.zoo_client
|
||||
.users()
|
||||
.get_self()
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!(e.to_string()))?;
|
||||
|
||||
// Hash the user's id.
|
||||
// Create a SHA-256 object
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
// Write input message
|
||||
hasher.update(user.id);
|
||||
// Read hash digest and consume hasher
|
||||
let result = hasher.finalize();
|
||||
// Get the hash as a string.
|
||||
let user_id_hash = format!("{:x}", result);
|
||||
|
||||
// Get the workspace folders.
|
||||
// The key of the workspace folder is the project name.
|
||||
let workspace_folders = self.workspace_folders();
|
||||
let project_names: Vec<String> = workspace_folders.iter().map(|v| v.name.clone()).collect::<Vec<_>>();
|
||||
// Get the first name.
|
||||
let project_name = project_names
|
||||
.first()
|
||||
.ok_or_else(|| anyhow::anyhow!("no project names"))?
|
||||
.to_string();
|
||||
|
||||
// Send the telemetry data.
|
||||
self.zoo_client
|
||||
.meta()
|
||||
.create_event(
|
||||
vec![kittycad::types::multipart::Attachment {
|
||||
// Clean the URI part.
|
||||
name: "attachment".to_string(),
|
||||
filename: Some("attachment.zip".to_string()),
|
||||
content_type: Some("application/x-zip".to_string()),
|
||||
data: self.create_zip()?,
|
||||
}],
|
||||
&kittycad::types::Event {
|
||||
// This gets generated server side so leave empty for now.
|
||||
attachment_uri: None,
|
||||
created_at: chrono::Utc::now(),
|
||||
event_type: kittycad::types::ModelingAppEventType::SuccessfulCompileBeforeClose,
|
||||
last_compiled_at: Some(chrono::Utc::now()),
|
||||
// We do not have project descriptions yet.
|
||||
project_description: None,
|
||||
project_name,
|
||||
// The UUID for the modeling app.
|
||||
// We can unwrap here because we know it will not panic.
|
||||
source_id: uuid::Uuid::from_str("70178592-dfca-47b3-bd2d-6fce2bcaee04").unwrap(),
|
||||
type_: kittycad::types::Type::ModelingAppEvent,
|
||||
user_id: user_id_hash,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!(e.to_string()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[tower_lsp::async_trait]
|
||||
@ -402,7 +485,32 @@ impl LanguageServer for Backend {
|
||||
}
|
||||
|
||||
async fn did_close(&self, params: DidCloseTextDocumentParams) {
|
||||
self.do_did_close(params).await
|
||||
self.do_did_close(params).await;
|
||||
|
||||
// Inject telemetry if we can train on the user's code.
|
||||
// Return early if we cannot.
|
||||
if !self.can_send_telemetry {
|
||||
return;
|
||||
}
|
||||
|
||||
// In wasm this needs to be spawn_local since fucking reqwests doesn't implement Send for wasm.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
let be = self.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
if let Err(err) = be.send_telemetry().await {
|
||||
be.client
|
||||
.log_message(MessageType::WARNING, format!("failed to send telemetry: {}", err))
|
||||
.await;
|
||||
}
|
||||
});
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
if let Err(err) = self.send_telemetry().await {
|
||||
self.client
|
||||
.log_message(MessageType::WARNING, format!("failed to send telemetry: {}", err))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn hover(&self, params: HoverParams) -> RpcResult<Option<Hover>> {
|
||||
|
@ -3,4 +3,6 @@
|
||||
mod backend;
|
||||
pub mod copilot;
|
||||
pub mod kcl;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod util;
|
||||
|
1359
src/wasm-lib/kcl/src/lsp/tests.rs
Normal file
1359
src/wasm-lib/kcl/src/lsp/tests.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -270,7 +270,8 @@ pub async fn copilot_lsp_run(config: ServerConfig, token: String, is_dev: bool)
|
||||
workspace_folders: Default::default(),
|
||||
current_code_map: Default::default(),
|
||||
editor_info: Arc::new(RwLock::new(kcl_lib::lsp::copilot::types::CopilotEditorInfo::default())),
|
||||
cache: kcl_lib::lsp::copilot::cache::CopilotCache::new(),
|
||||
cache: Arc::new(kcl_lib::lsp::copilot::cache::CopilotCache::new()),
|
||||
telemetry: Default::default(),
|
||||
zoo_client,
|
||||
})
|
||||
.custom_method("setEditorInfo", kcl_lib::lsp::copilot::Backend::set_editor_info)
|
||||
@ -278,7 +279,7 @@ pub async fn copilot_lsp_run(config: ServerConfig, token: String, is_dev: bool)
|
||||
"getCompletions",
|
||||
kcl_lib::lsp::copilot::Backend::get_completions_cycling,
|
||||
)
|
||||
.custom_method("notifyAccepted", kcl_lib::lsp::copilot::Backend::accept_completions)
|
||||
.custom_method("notifyAccepted", kcl_lib::lsp::copilot::Backend::accept_completion)
|
||||
.custom_method("notifyRejected", kcl_lib::lsp::copilot::Backend::reject_completions)
|
||||
.finish();
|
||||
|
||||
|
@ -28,7 +28,7 @@ async fn execute_and_snapshot(code: &str, units: kittycad::types::UnitLength) ->
|
||||
|
||||
let ws = client
|
||||
.modeling()
|
||||
.commands_ws(None, None, None, None, Some(false))
|
||||
.commands_ws(None, None, None, None, None, Some(false))
|
||||
.await?;
|
||||
|
||||
// Create a temporary file to write the output to.
|
||||
|
@ -30,7 +30,7 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid
|
||||
|
||||
let ws = client
|
||||
.modeling()
|
||||
.commands_ws(None, None, None, None, Some(false))
|
||||
.commands_ws(None, None, None, None, None, Some(false))
|
||||
.await?;
|
||||
|
||||
let tokens = kcl_lib::token::lexer(code);
|
||||
|
Reference in New Issue
Block a user