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
|
shell: bash
|
||||||
run: |-
|
run: |-
|
||||||
cd "${{ matrix.dir }}"
|
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:
|
env:
|
||||||
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
|
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
|
||||||
RUST_MIN_STACK: 10485760000
|
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 filePath = useAbsoluteFilePath()
|
||||||
const { onProjectOpen } = useLspContext()
|
const { onProjectOpen } = useLspContext()
|
||||||
|
|
||||||
|
const projectName = project?.name || null
|
||||||
|
const projectPath = project?.path || null
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onProjectOpen(project || null, file || null)
|
onProjectOpen({ name: projectName, path: projectPath }, file || null)
|
||||||
}, [])
|
}, [projectName, projectPath])
|
||||||
|
|
||||||
useHotKeyListener()
|
useHotKeyListener()
|
||||||
const {
|
const {
|
||||||
|
@ -14,7 +14,6 @@ import { LanguageSupport } from '@codemirror/language'
|
|||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { paths } from 'lib/paths'
|
import { paths } from 'lib/paths'
|
||||||
import { FileEntry } from '@tauri-apps/api/fs'
|
import { FileEntry } from '@tauri-apps/api/fs'
|
||||||
import { ProjectWithEntryPointMetadata } from 'lib/types'
|
|
||||||
|
|
||||||
const DEFAULT_FILE_NAME: string = 'main.kcl'
|
const DEFAULT_FILE_NAME: string = 'main.kcl'
|
||||||
|
|
||||||
@ -40,7 +39,7 @@ type LspContext = {
|
|||||||
redirect: boolean
|
redirect: boolean
|
||||||
) => void
|
) => void
|
||||||
onProjectOpen: (
|
onProjectOpen: (
|
||||||
project: ProjectWithEntryPointMetadata | null,
|
project: { name: string | null; path: string | null } | null,
|
||||||
file: FileEntry | null
|
file: FileEntry | null
|
||||||
) => void
|
) => void
|
||||||
onFileOpen: (filePath: string | null, projectPath: string | 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 { auth } = useSettingsAuthContext()
|
||||||
|
const token = auth?.context?.token
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
// So this is a bit weird, we need to initialize the lsp server and client.
|
// 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)
|
const client = new Client(fromServer, intoServer)
|
||||||
if (!TEST) {
|
if (!TEST) {
|
||||||
Server.initialize(intoServer, fromServer).then((lspServer) => {
|
Server.initialize(intoServer, fromServer).then((lspServer) => {
|
||||||
const token = auth?.context?.token
|
|
||||||
lspServer.start('kcl', token)
|
lspServer.start('kcl', token)
|
||||||
setIsKclLspServerReady(true)
|
setIsKclLspServerReady(true)
|
||||||
})
|
})
|
||||||
@ -88,7 +87,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
|
|
||||||
const lspClient = new LanguageServerClient({ client, name: 'kcl' })
|
const lspClient = new LanguageServerClient({ client, name: 'kcl' })
|
||||||
return { lspClient }
|
return { lspClient }
|
||||||
}, [setIsKclLspServerReady])
|
}, [setIsKclLspServerReady, token])
|
||||||
|
|
||||||
// Here we initialize the plugin which will start the client.
|
// 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
|
// 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)
|
const client = new Client(fromServer, intoServer)
|
||||||
if (!TEST) {
|
if (!TEST) {
|
||||||
Server.initialize(intoServer, fromServer).then((lspServer) => {
|
Server.initialize(intoServer, fromServer).then((lspServer) => {
|
||||||
const token = auth?.context?.token
|
|
||||||
lspServer.start('copilot', token)
|
lspServer.start('copilot', token)
|
||||||
setIsCopilotLspServerReady(true)
|
setIsCopilotLspServerReady(true)
|
||||||
})
|
})
|
||||||
@ -124,7 +122,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
|
|
||||||
const lspClient = new LanguageServerClient({ client, name: 'copilot' })
|
const lspClient = new LanguageServerClient({ client, name: 'copilot' })
|
||||||
return { lspClient }
|
return { lspClient }
|
||||||
}, [setIsCopilotLspServerReady])
|
}, [setIsCopilotLspServerReady, token])
|
||||||
|
|
||||||
// Here we initialize the plugin which will start the client.
|
// 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
|
// 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 = (
|
const onProjectOpen = (
|
||||||
project: ProjectWithEntryPointMetadata | null,
|
project: { name: string | null; path: string | null } | null,
|
||||||
file: FileEntry | null
|
file: FileEntry | null
|
||||||
) => {
|
) => {
|
||||||
const projectName = project?.name || 'ProjectRoot'
|
const projectName = project?.name || 'ProjectRoot'
|
||||||
|
@ -2,67 +2,10 @@ import type * as LSP from 'vscode-languageserver-protocol'
|
|||||||
import Client from './client'
|
import Client from './client'
|
||||||
import { SemanticToken, deserializeTokens } from './kcl/semantic_tokens'
|
import { SemanticToken, deserializeTokens } from './kcl/semantic_tokens'
|
||||||
import { LanguageServerPlugin } from 'editor/plugins/lsp/plugin'
|
import { LanguageServerPlugin } from 'editor/plugins/lsp/plugin'
|
||||||
|
import { CopilotLspCompletionParams } from 'wasm-lib/kcl/bindings/CopilotLspCompletionParams'
|
||||||
export interface CopilotGetCompletionsParams {
|
import { CopilotCompletionResponse } from 'wasm-lib/kcl/bindings/CopilotCompletionResponse'
|
||||||
doc: {
|
import { CopilotAcceptCompletionParams } from 'wasm-lib/kcl/bindings/CopilotAcceptCompletionParams'
|
||||||
source: string
|
import { CopilotRejectCompletionParams } from 'wasm-lib/kcl/bindings/CopilotRejectCompletionParams'
|
||||||
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[]
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://microsoft.github.io/language-server-protocol/specifications/specification-current/
|
// https://microsoft.github.io/language-server-protocol/specifications/specification-current/
|
||||||
|
|
||||||
@ -78,7 +21,7 @@ interface LSPRequestMap {
|
|||||||
LSP.SemanticTokensParams,
|
LSP.SemanticTokensParams,
|
||||||
LSP.SemanticTokens
|
LSP.SemanticTokens
|
||||||
]
|
]
|
||||||
getCompletions: [CopilotGetCompletionsParams, CopilotGetCompletionsResult]
|
getCompletions: [CopilotLspCompletionParams, CopilotCompletionResponse]
|
||||||
notifyAccepted: [CopilotAcceptCompletionParams, any]
|
notifyAccepted: [CopilotAcceptCompletionParams, any]
|
||||||
notifyRejected: [CopilotRejectCompletionParams, any]
|
notifyRejected: [CopilotRejectCompletionParams, any]
|
||||||
}
|
}
|
||||||
@ -271,7 +214,7 @@ export class LanguageServerClient {
|
|||||||
return this.client.notify(method, params)
|
return this.client.notify(method, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCompletion(params: CopilotGetCompletionsParams) {
|
async getCompletion(params: CopilotLspCompletionParams) {
|
||||||
const response = await this.request('getCompletions', params)
|
const response = await this.request('getCompletions', params)
|
||||||
//
|
//
|
||||||
this.queuedUids = [...response.completions.map((c) => c.uuid)]
|
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 { useHotkeys } from 'react-hotkeys-hook'
|
||||||
import { isTauri } from 'lib/isTauri'
|
import { isTauri } from 'lib/isTauri'
|
||||||
import { kclManager } from 'lang/KclSingleton'
|
import { kclManager } from 'lang/KclSingleton'
|
||||||
|
import { useLspContext } from 'components/LspProvider'
|
||||||
|
|
||||||
// This route only opens in the Tauri desktop context for now,
|
// 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.
|
// as defined in Router.tsx, so we can use the Tauri APIs and types.
|
||||||
@ -51,6 +52,7 @@ const Home = () => {
|
|||||||
send: sendToSettings,
|
send: sendToSettings,
|
||||||
},
|
},
|
||||||
} = useSettingsAuthContext()
|
} = useSettingsAuthContext()
|
||||||
|
const { onProjectOpen } = useLspContext()
|
||||||
|
|
||||||
// Set the default directory if it's been updated
|
// Set the default directory if it's been updated
|
||||||
// during the loading of the home page. This is wrapped
|
// during the loading of the home page. This is wrapped
|
||||||
@ -84,12 +86,16 @@ const Home = () => {
|
|||||||
event: EventFrom<typeof homeMachine>
|
event: EventFrom<typeof homeMachine>
|
||||||
) => {
|
) => {
|
||||||
if (event.data && 'name' in event.data) {
|
if (event.data && 'name' in event.data) {
|
||||||
commandBarSend({ type: 'Close' })
|
let projectPath = context.defaultDirectory + sep + event.data.name
|
||||||
navigate(
|
onProjectOpen(
|
||||||
`${paths.FILE}/${encodeURIComponent(
|
{
|
||||||
context.defaultDirectory + sep + event.data.name
|
name: event.data.name,
|
||||||
)}`
|
path: projectPath,
|
||||||
|
},
|
||||||
|
null
|
||||||
)
|
)
|
||||||
|
commandBarSend({ type: 'Close' })
|
||||||
|
navigate(`${paths.FILE}/${encodeURIComponent(projectPath)}`)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toastSuccess: (_, event) => toast.success((event.data || '') + ''),
|
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]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.34"
|
version = "0.4.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b"
|
checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android-tzdata",
|
"android-tzdata",
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
@ -1914,6 +1914,7 @@ dependencies = [
|
|||||||
"async-recursion",
|
"async-recursion",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bson",
|
"bson",
|
||||||
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"criterion",
|
"criterion",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
@ -1929,6 +1930,7 @@ dependencies = [
|
|||||||
"kittycad-execution-plan-macros",
|
"kittycad-execution-plan-macros",
|
||||||
"kittycad-execution-plan-traits",
|
"kittycad-execution-plan-traits",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"mime_guess",
|
||||||
"parse-display 0.9.0",
|
"parse-display 0.9.0",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -1936,6 +1938,7 @@ dependencies = [
|
|||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"sha2",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-tungstenite",
|
"tokio-tungstenite",
|
||||||
@ -1946,6 +1949,7 @@ dependencies = [
|
|||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"winnow",
|
"winnow",
|
||||||
|
"zip",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1962,9 +1966,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad"
|
name = "kittycad"
|
||||||
version = "0.2.59"
|
version = "0.2.60"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4080db4364c103601db486e4a8aa889ea56c011991e4c454373d8050a165d3da"
|
checksum = "f8aa5906d0730bd90f6b3331fe57c04951d00743169a29ee96408767b4060605"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1978,6 +1982,7 @@ dependencies = [
|
|||||||
"http 0.2.9",
|
"http 0.2.9",
|
||||||
"itertools 0.10.5",
|
"itertools 0.10.5",
|
||||||
"log",
|
"log",
|
||||||
|
"mime_guess",
|
||||||
"parse-display 0.8.2",
|
"parse-display 0.8.2",
|
||||||
"phonenumber",
|
"phonenumber",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
@ -2000,7 +2005,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad-execution-plan"
|
name = "kittycad-execution-plan"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"insta",
|
"insta",
|
||||||
@ -2020,7 +2025,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad-execution-plan-macros"
|
name = "kittycad-execution-plan-macros"
|
||||||
version = "0.1.8"
|
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 = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -2030,7 +2035,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad-execution-plan-traits"
|
name = "kittycad-execution-plan-traits"
|
||||||
version = "0.1.12"
|
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 = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@ -2040,7 +2045,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad-modeling-cmds"
|
name = "kittycad-modeling-cmds"
|
||||||
version = "0.1.28"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -2068,7 +2073,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad-modeling-cmds-macros"
|
name = "kittycad-modeling-cmds-macros"
|
||||||
version = "0.1.2"
|
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 = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -2077,8 +2082,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad-modeling-session"
|
name = "kittycad-modeling-session"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#c554cbeda3f217c1baab8a33ffad50e2ecdc8ab9"
|
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#c7722adf9744b9e4eead0a7a88662ad5e7e3adbf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"kittycad",
|
"kittycad",
|
||||||
@ -5505,6 +5510,17 @@ dependencies = [
|
|||||||
"syn 2.0.52",
|
"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]]
|
[[package]]
|
||||||
name = "zune-inflate"
|
name = "zune-inflate"
|
||||||
version = "0.2.54"
|
version = "0.2.54"
|
||||||
|
@ -59,7 +59,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[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 = { 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-macros = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
|
||||||
kittycad-execution-plan-traits = { 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"] }
|
anyhow = { version = "1.0.80", features = ["backtrace"] }
|
||||||
async-recursion = "1.0.5"
|
async-recursion = "1.0.5"
|
||||||
async-trait = "0.1.77"
|
async-trait = "0.1.77"
|
||||||
|
chrono = "0.4.35"
|
||||||
clap = { version = "4.5.2", features = ["cargo", "derive", "env", "unicode"], optional = true }
|
clap = { version = "4.5.2", features = ["cargo", "derive", "env", "unicode"], optional = true }
|
||||||
dashmap = "5.5.3"
|
dashmap = "5.5.3"
|
||||||
databake = { version = "0.1.7", features = ["derive"] }
|
databake = { version = "0.1.7", features = ["derive"] }
|
||||||
@ -25,16 +26,19 @@ kittycad = { workspace = true }
|
|||||||
kittycad-execution-plan-macros = { workspace = true }
|
kittycad-execution-plan-macros = { workspace = true }
|
||||||
kittycad-execution-plan-traits = { workspace = true }
|
kittycad-execution-plan-traits = { workspace = true }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
mime_guess = "2.0.4"
|
||||||
parse-display = "0.9.0"
|
parse-display = "0.9.0"
|
||||||
reqwest = { version = "0.11.26", default-features = false, features = ["stream", "rustls-tls"] }
|
reqwest = { version = "0.11.26", default-features = false, features = ["stream", "rustls-tls"] }
|
||||||
ropey = "1.6.1"
|
ropey = "1.6.1"
|
||||||
schemars = { version = "0.8.16", features = ["impl_json_schema", "url", "uuid1"] }
|
schemars = { version = "0.8.16", features = ["impl_json_schema", "url", "uuid1"] }
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
serde_json = "1.0.114"
|
serde_json = "1.0.114"
|
||||||
|
sha2 = "0.10.8"
|
||||||
thiserror = "1.0.57"
|
thiserror = "1.0.57"
|
||||||
ts-rs = { version = "7.1.1", features = ["uuid-impl"] }
|
ts-rs = { version = "7.1.1", features = ["uuid-impl"] }
|
||||||
uuid = { version = "1.7.0", features = ["v4", "js", "serde"] }
|
uuid = { version = "1.7.0", features = ["v4", "js", "serde"] }
|
||||||
winnow = "0.5.40"
|
winnow = "0.5.40"
|
||||||
|
zip = { version = "0.6.6", default-features = false }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
js-sys = { version = "0.3.69" }
|
js-sys = { version = "0.3.69" }
|
||||||
|
@ -75,11 +75,33 @@ pub trait Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn do_did_change_workspace_folders(&self, params: DidChangeWorkspaceFoldersParams) {
|
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.add_workspace_folders(params.event.added.clone());
|
||||||
self.remove_workspace_folders(params.event.removed);
|
self.remove_workspace_folders(params.event.removed);
|
||||||
// Remove the code from the current code map.
|
// 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.
|
// We do this since it means the user is changing projects so let's refresh the state.
|
||||||
|
if !self.current_code_map().is_empty() && should_clear {
|
||||||
self.clear_code_state();
|
self.clear_code_state();
|
||||||
|
}
|
||||||
for added in params.event.added {
|
for added in params.event.added {
|
||||||
// Try to read all the files in the project.
|
// Try to read all the files in the project.
|
||||||
let project_dir = added.uri.to_string().replace("file://", "");
|
let project_dir = added.uri.to_string().replace("file://", "");
|
||||||
@ -181,14 +203,5 @@ pub trait Backend {
|
|||||||
self.client()
|
self.client()
|
||||||
.log_message(MessageType::INFO, format!("document closed: {:?}", params))
|
.log_message(MessageType::INFO, format!("document closed: {:?}", params))
|
||||||
.await;
|
.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},
|
copilot::types::{CopilotCompletionResponse, CopilotEditorInfo, CopilotLspCompletionParams, DocParams},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use self::types::{CopilotAcceptCompletionParams, CopilotCompletionTelemetry, CopilotRejectCompletionParams};
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
pub struct Success {
|
pub struct Success {
|
||||||
success: bool,
|
success: bool,
|
||||||
@ -39,7 +41,7 @@ impl Success {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Backend {
|
pub struct Backend {
|
||||||
/// The client is used to send notifications and requests to the client.
|
/// The client is used to send notifications and requests to the client.
|
||||||
pub client: tower_lsp::Client,
|
pub client: tower_lsp::Client,
|
||||||
@ -54,7 +56,9 @@ pub struct Backend {
|
|||||||
/// The editor info is used to store information about the editor.
|
/// The editor info is used to store information about the editor.
|
||||||
pub editor_info: Arc<RwLock<CopilotEditorInfo>>,
|
pub editor_info: Arc<RwLock<CopilotEditorInfo>>,
|
||||||
/// The cache is used to store the results of previous requests.
|
/// 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.
|
// Implement the shared backend trait for the language server.
|
||||||
@ -158,7 +162,7 @@ impl Backend {
|
|||||||
let pos = params.doc.position;
|
let pos = params.doc.position;
|
||||||
let uri = params.doc.uri.to_string();
|
let uri = params.doc.uri.to_string();
|
||||||
let rope = ropey::Rope::from_str(¶ms.doc.source);
|
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 {
|
Ok(DocParams {
|
||||||
uri: uri.to_string(),
|
uri: uri.to_string(),
|
||||||
@ -166,7 +170,7 @@ impl Backend {
|
|||||||
language: params.doc.language_id.to_string(),
|
language: params.doc.language_id.to_string(),
|
||||||
prefix: crate::lsp::util::get_text_before(offset, &rope).unwrap_or_default(),
|
prefix: crate::lsp::util::get_text_before(offset, &rope).unwrap_or_default(),
|
||||||
suffix: crate::lsp::util::get_text_after(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,
|
rope,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -185,37 +189,69 @@ impl Backend {
|
|||||||
let line_before = doc_params.line_before.to_string();
|
let line_before = doc_params.line_before.to_string();
|
||||||
|
|
||||||
// Let's not call it yet since it's not our model.
|
// Let's not call it yet since it's not our model.
|
||||||
/*let completion_list = self
|
// 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)
|
.get_completions(doc_params.language, doc_params.prefix, doc_params.suffix)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| Error {
|
.map_err(|err| Error {
|
||||||
code: tower_lsp::jsonrpc::ErrorCode::from(69),
|
code: tower_lsp::jsonrpc::ErrorCode::from(69),
|
||||||
data: None,
|
data: None,
|
||||||
message: Cow::from(format!("Failed to get completions: {}", err)),
|
message: Cow::from(format!("Failed to get completions: {}", err)),
|
||||||
})?;*/
|
})?;
|
||||||
|
#[cfg(not(test))]
|
||||||
let completion_list = vec![];
|
let completion_list = vec![];
|
||||||
|
|
||||||
let response = CopilotCompletionResponse::from_str_vec(completion_list, line_before, doc_params.pos);
|
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
|
self.cache
|
||||||
.set_cached_result(&doc_params.uri, &doc_params.pos.line, &response);
|
.set_cached_result(&doc_params.uri, &doc_params.pos.line, &response);
|
||||||
|
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn accept_completions(&self, params: Vec<String>) {
|
pub async fn accept_completion(&self, params: CopilotAcceptCompletionParams) {
|
||||||
self.client
|
self.client
|
||||||
.log_message(MessageType::INFO, format!("Accepted completions: {:?}", params))
|
.log_message(MessageType::INFO, format!("Accepted completions: {:?}", params))
|
||||||
.await;
|
.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
|
self.client
|
||||||
.log_message(MessageType::INFO, format!("Rejected completions: {:?}", params))
|
.log_message(MessageType::INFO, format!("Rejected completions: {:?}", params))
|
||||||
.await;
|
.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 ropey::Rope;
|
||||||
use serde::{Deserialize, Serialize};
|
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")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CopilotCyclingCompletion {
|
#[ts(export)]
|
||||||
pub display_text: String, // partial text
|
pub struct CopilotPosition {
|
||||||
pub text: String, // fulltext
|
/// Line position in a document (zero-based).
|
||||||
pub range: Range, // start char always 0
|
pub line: u32,
|
||||||
pub position: Position,
|
/// 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 struct Choices {
|
||||||
pub text: String,
|
pub text: String,
|
||||||
pub index: i16,
|
pub index: i16,
|
||||||
@ -21,14 +61,16 @@ pub struct Choices {
|
|||||||
pub logprobs: Option<String>,
|
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 struct CopilotCompletionResponse {
|
||||||
pub completions: Vec<CopilotCyclingCompletion>,
|
pub completions: Vec<CopilotCyclingCompletion>,
|
||||||
pub cancellation_reason: Option<String>,
|
pub cancellation_reason: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CopilotCompletionResponse {
|
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
|
let completions = str_vec
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| CopilotCyclingCompletion::new(x.to_string(), line_before.to_string(), pos))
|
.map(|x| CopilotCyclingCompletion::new(x.to_string(), line_before.to_string(), pos))
|
||||||
@ -41,19 +83,20 @@ impl CopilotCompletionResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CopilotCyclingCompletion {
|
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 display_text = text.clone();
|
||||||
let text = format!("{}{}", line_before, text);
|
let text = format!("{}{}", line_before, text);
|
||||||
let end_char = text.find('\n').unwrap_or(text.len()) as u32;
|
let end_char = text.find('\n').unwrap_or(text.len()) as u32;
|
||||||
Self {
|
Self {
|
||||||
|
uuid: uuid::Uuid::new_v4(),
|
||||||
display_text, // partial text
|
display_text, // partial text
|
||||||
text, // fulltext
|
text, // fulltext
|
||||||
range: Range {
|
range: CopilotRange {
|
||||||
start: Position {
|
start: CopilotPosition {
|
||||||
character: 0,
|
character: 0,
|
||||||
line: position.line,
|
line: position.line,
|
||||||
},
|
},
|
||||||
end: Position {
|
end: CopilotPosition {
|
||||||
character: end_char,
|
character: end_char,
|
||||||
line: position.line,
|
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")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct LanguageEntry {
|
#[ts(export)]
|
||||||
language_id: String,
|
pub struct LanguageEntry {
|
||||||
|
pub language_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone, ts_rs::TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct EditorConfiguration {
|
#[ts(export)]
|
||||||
disabled_languages: Vec<LanguageEntry>,
|
pub struct EditorConfiguration {
|
||||||
enable_auto_completions: bool,
|
pub disabled_languages: Vec<LanguageEntry>,
|
||||||
|
pub enable_auto_completions: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for EditorConfiguration {
|
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")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct EditorInfo {
|
#[ts(export)]
|
||||||
name: String,
|
pub struct EditorInfo {
|
||||||
version: String,
|
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")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[ts(export)]
|
||||||
pub struct CopilotEditorInfo {
|
pub struct CopilotEditorInfo {
|
||||||
editor_configuration: EditorConfiguration,
|
pub editor_configuration: EditorConfiguration,
|
||||||
editor_info: EditorInfo,
|
pub editor_info: EditorInfo,
|
||||||
editor_plugin_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 {
|
pub struct DocParams {
|
||||||
|
#[serde(skip)]
|
||||||
pub rope: Rope,
|
pub rope: Rope,
|
||||||
pub uri: String,
|
pub uri: String,
|
||||||
pub pos: Position,
|
pub pos: CopilotPosition,
|
||||||
pub language: String,
|
pub language: String,
|
||||||
pub line_before: String,
|
pub line_before: String,
|
||||||
pub prefix: String,
|
pub prefix: String,
|
||||||
pub suffix: String,
|
pub suffix: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Default, Serialize, Deserialize, Clone, ts_rs::TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[ts(export)]
|
||||||
pub struct CopilotLspCompletionParams {
|
pub struct CopilotLspCompletionParams {
|
||||||
pub doc: CopilotDocParams,
|
pub doc: CopilotDocParams,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Default, Serialize, Deserialize, Clone, ts_rs::TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[ts(export)]
|
||||||
pub struct CopilotDocParams {
|
pub struct CopilotDocParams {
|
||||||
pub indent_size: u32,
|
pub indent_size: u32,
|
||||||
pub insert_spaces: bool,
|
pub insert_spaces: bool,
|
||||||
pub language_id: String,
|
pub language_id: String,
|
||||||
pub path: String,
|
pub path: String,
|
||||||
pub position: Position,
|
pub position: CopilotPosition,
|
||||||
pub relative_path: String,
|
pub relative_path: String,
|
||||||
pub source: String,
|
pub source: String,
|
||||||
pub tab_size: u32,
|
pub tab_size: u32,
|
||||||
pub uri: String,
|
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.
|
//! Functions for the `kcl` lsp server.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, io::Write, str::FromStr};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
#[cfg(feature = "cli")]
|
#[cfg(feature = "cli")]
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
|
use sha2::Digest;
|
||||||
use tower_lsp::{
|
use tower_lsp::{
|
||||||
jsonrpc::Result as RpcResult,
|
jsonrpc::Result as RpcResult,
|
||||||
lsp_types::{
|
lsp_types::{
|
||||||
@ -44,6 +45,7 @@ pub struct Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The lsp server backend.
|
/// The lsp server backend.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Backend {
|
pub struct Backend {
|
||||||
/// The client for the backend.
|
/// The client for the backend.
|
||||||
pub client: Client,
|
pub client: Client,
|
||||||
@ -285,6 +287,87 @@ impl Backend {
|
|||||||
|
|
||||||
completions
|
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]
|
#[tower_lsp::async_trait]
|
||||||
@ -402,7 +485,32 @@ impl LanguageServer for Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn did_close(&self, params: DidCloseTextDocumentParams) {
|
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>> {
|
async fn hover(&self, params: HoverParams) -> RpcResult<Option<Hover>> {
|
||||||
|
@ -3,4 +3,6 @@
|
|||||||
mod backend;
|
mod backend;
|
||||||
pub mod copilot;
|
pub mod copilot;
|
||||||
pub mod kcl;
|
pub mod kcl;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
mod util;
|
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(),
|
workspace_folders: Default::default(),
|
||||||
current_code_map: Default::default(),
|
current_code_map: Default::default(),
|
||||||
editor_info: Arc::new(RwLock::new(kcl_lib::lsp::copilot::types::CopilotEditorInfo::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,
|
zoo_client,
|
||||||
})
|
})
|
||||||
.custom_method("setEditorInfo", kcl_lib::lsp::copilot::Backend::set_editor_info)
|
.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",
|
"getCompletions",
|
||||||
kcl_lib::lsp::copilot::Backend::get_completions_cycling,
|
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)
|
.custom_method("notifyRejected", kcl_lib::lsp::copilot::Backend::reject_completions)
|
||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ async fn execute_and_snapshot(code: &str, units: kittycad::types::UnitLength) ->
|
|||||||
|
|
||||||
let ws = client
|
let ws = client
|
||||||
.modeling()
|
.modeling()
|
||||||
.commands_ws(None, None, None, None, Some(false))
|
.commands_ws(None, None, None, None, None, Some(false))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Create a temporary file to write the output to.
|
// 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
|
let ws = client
|
||||||
.modeling()
|
.modeling()
|
||||||
.commands_ws(None, None, None, None, Some(false))
|
.commands_ws(None, None, None, None, None, Some(false))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let tokens = kcl_lib::token::lexer(code);
|
let tokens = kcl_lib::token::lexer(code);
|
||||||
|
Reference in New Issue
Block a user