diff --git a/crates/yaak-plugins/bindings/gen_events.ts b/crates/yaak-plugins/bindings/gen_events.ts index 3b3b42d0..9c273e99 100644 --- a/crates/yaak-plugins/bindings/gen_events.ts +++ b/crates/yaak-plugins/bindings/gen_events.ts @@ -66,7 +66,7 @@ export type DeleteModelRequest = { model: string, id: string, }; export type DeleteModelResponse = { model: AnyModel, }; -export type EditorLanguage = "text" | "javascript" | "json" | "html" | "xml" | "graphql" | "markdown"; +export type EditorLanguage = "text" | "javascript" | "json" | "html" | "xml" | "graphql" | "markdown" | "c" | "clojure" | "csharp" | "go" | "java" | "kotlin" | "objective_c" | "ocaml" | "php" | "powershell" | "python" | "r" | "ruby" | "shell" | "swift"; export type EmptyPayload = {}; diff --git a/crates/yaak-plugins/src/events.rs b/crates/yaak-plugins/src/events.rs index 34889e58..6a75c2c3 100644 --- a/crates/yaak-plugins/src/events.rs +++ b/crates/yaak-plugins/src/events.rs @@ -951,6 +951,21 @@ pub enum EditorLanguage { Xml, Graphql, Markdown, + C, + Clojure, + Csharp, + Go, + Java, + Kotlin, + ObjectiveC, + Ocaml, + Php, + Powershell, + Python, + R, + Ruby, + Shell, + Swift, } impl Default for EditorLanguage { diff --git a/package-lock.json b/package-lock.json index b612f0a8..0ad5040d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,6 +63,13 @@ "crates/yaak-ws", "src-web" ], + "dependencies": { + "@codemirror/lang-go": "^6.0.1", + "@codemirror/lang-java": "^6.0.2", + "@codemirror/lang-php": "^6.0.2", + "@codemirror/lang-python": "^6.2.1", + "@codemirror/legacy-modes": "^6.5.2" + }, "devDependencies": { "@biomejs/biome": "^2.3.13", "@tauri-apps/cli": "^2.9.6", @@ -737,6 +744,19 @@ "@lezer/css": "^1.1.7" } }, + "node_modules/@codemirror/lang-go": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-go/-/lang-go-6.0.1.tgz", + "integrity": "sha512-7fNvbyNylvqCphW9HD6WFnRpcDjr+KXX/FgqXy5H5ZS0eC5edDljukm/yNgYkwTsgp2busdod50AOTIy6Jikfg==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/go": "^1.0.0" + } + }, "node_modules/@codemirror/lang-html": { "version": "6.4.11", "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.11.tgz", @@ -754,6 +774,16 @@ "@lezer/html": "^1.3.12" } }, + "node_modules/@codemirror/lang-java": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-java/-/lang-java-6.0.2.tgz", + "integrity": "sha512-m5Nt1mQ/cznJY7tMfQTJchmrjdjQ71IDs+55d1GAa8DGaB8JXWsVCkVT284C3RTASaY43YknrK2X3hPO/J3MOQ==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/java": "^1.0.0" + } + }, "node_modules/@codemirror/lang-javascript": { "version": "6.2.4", "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.4.tgz", @@ -794,6 +824,32 @@ "@lezer/markdown": "^1.0.0" } }, + "node_modules/@codemirror/lang-php": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-php/-/lang-php-6.0.2.tgz", + "integrity": "sha512-ZKy2v1n8Fc8oEXj0Th0PUMXzQJ0AIR6TaZU+PbDHExFwdu+guzOA4jmCHS1Nz4vbFezwD7LyBdDnddSJeScMCA==", + "license": "MIT", + "dependencies": { + "@codemirror/lang-html": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/php": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-python": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-python/-/lang-python-6.2.1.tgz", + "integrity": "sha512-IRjC8RUBhn9mGR9ywecNhB51yePWCGgvHfY1lWN/Mrp3cKuHr0isDKia+9HnvhiWNnMpbGhWrkhuWOc09exRyw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.3.2", + "@codemirror/language": "^6.8.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.2.1", + "@lezer/python": "^1.1.4" + } + }, "node_modules/@codemirror/lang-xml": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.1.0.tgz", @@ -837,6 +893,15 @@ "style-mod": "^4.0.0" } }, + "node_modules/@codemirror/legacy-modes": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.5.2.tgz", + "integrity": "sha512-/jJbwSTazlQEDOQw2FJ8LEEKVS72pU0lx6oM54kGpL8t/NJ2Jda3CZ4pcltiKTdqYSRk3ug1B3pil1gsjA6+8Q==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0" + } + }, "node_modules/@codemirror/lint": { "version": "6.9.2", "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.2.tgz", @@ -1571,6 +1636,17 @@ "lezer-generator": "src/lezer-generator.cjs" } }, + "node_modules/@lezer/go": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@lezer/go/-/go-1.0.1.tgz", + "integrity": "sha512-xToRsYxwsgJNHTgNdStpcvmbVuKxTapV0dM0wey1geMMRc9aggoVyKgzYp41D2/vVOx+Ii4hmE206kvxIXBVXQ==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.0" + } + }, "node_modules/@lezer/highlight": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", @@ -1591,6 +1667,17 @@ "@lezer/lr": "^1.0.0" } }, + "node_modules/@lezer/java": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@lezer/java/-/java-1.1.3.tgz", + "integrity": "sha512-yHquUfujwg6Yu4Fd1GNHCvidIvJwi/1Xu2DaKl/pfWIA2c1oXkVvawH3NyXhCaFx4OdlYBVX5wvz2f7Aoa/4Xw==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, "node_modules/@lezer/javascript": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz", @@ -1632,6 +1719,28 @@ "@lezer/highlight": "^1.0.0" } }, + "node_modules/@lezer/php": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@lezer/php/-/php-1.0.5.tgz", + "integrity": "sha512-W7asp9DhM6q0W6DYNwIkLSKOvxlXRrif+UXBMxzsJUuqmhE7oVU+gS3THO4S/Puh7Xzgm858UNaFi6dxTP8dJA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.1.0" + } + }, + "node_modules/@lezer/python": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/@lezer/python/-/python-1.1.18.tgz", + "integrity": "sha512-31FiUrU7z9+d/ElGQLJFXl+dKOdx0jALlP3KEOsGTex8mvj+SoE1FgItcHWK/axkxCHGUSpqIHt6JAWfWu9Rhg==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, "node_modules/@lezer/xml": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.6.tgz", diff --git a/package.json b/package.json index 5a302944..2501b08a 100644 --- a/package.json +++ b/package.json @@ -105,5 +105,12 @@ "npm-run-all": "^4.1.5", "typescript": "^5.8.3", "vitest": "^3.2.4" + }, + "dependencies": { + "@codemirror/lang-go": "^6.0.1", + "@codemirror/lang-java": "^6.0.2", + "@codemirror/lang-php": "^6.0.2", + "@codemirror/lang-python": "^6.2.1", + "@codemirror/legacy-modes": "^6.5.2" } } diff --git a/packages/plugin-runtime-types/src/bindings/gen_events.ts b/packages/plugin-runtime-types/src/bindings/gen_events.ts index 3b3b42d0..9c273e99 100644 --- a/packages/plugin-runtime-types/src/bindings/gen_events.ts +++ b/packages/plugin-runtime-types/src/bindings/gen_events.ts @@ -66,7 +66,7 @@ export type DeleteModelRequest = { model: string, id: string, }; export type DeleteModelResponse = { model: AnyModel, }; -export type EditorLanguage = "text" | "javascript" | "json" | "html" | "xml" | "graphql" | "markdown"; +export type EditorLanguage = "text" | "javascript" | "json" | "html" | "xml" | "graphql" | "markdown" | "c" | "clojure" | "csharp" | "go" | "java" | "kotlin" | "objective_c" | "ocaml" | "php" | "powershell" | "python" | "r" | "ruby" | "shell" | "swift"; export type EmptyPayload = {}; diff --git a/plugins-external/httpsnippet/src/index.ts b/plugins-external/httpsnippet/src/index.ts index 3550baf0..e44a6907 100644 --- a/plugins-external/httpsnippet/src/index.ts +++ b/plugins-external/httpsnippet/src/index.ts @@ -1,4 +1,4 @@ -import { availableTargets, HTTPSnippet } from '@readme/httpsnippet'; +import { type HarRequest, availableTargets, HTTPSnippet } from '@readme/httpsnippet'; import type { HttpRequest, PluginDefinition } from '@yaakapp/api'; // Get all available targets and build select options @@ -10,31 +10,64 @@ const languageOptions = targets.map((target) => ({ value: target.key, })); +// Preferred clients per target (shown first in the list) +const preferredClients: Record = { + javascript: 'fetch', + node: 'fetch', +}; + // Get client options for a given target key function getClientOptions(targetKey: string) { const target = targets.find((t) => t.key === targetKey); if (!target) return []; - return target.clients.map((client) => ({ - label: client.title, - value: client.key, - })); + const preferred = preferredClients[targetKey]; + return target.clients + .map((client) => ({ + label: client.title, + value: client.key, + })) + .sort((a, b) => { + if (a.value === preferred) return -1; + if (b.value === preferred) return 1; + return 0; + }); } // Get default client for a target function getDefaultClient(targetKey: string): string { - const target = targets.find((t) => t.key === targetKey); - return target?.clients[0]?.key ?? ''; + const options = getClientOptions(targetKey); + return options[0]?.value ?? ''; } // Defaults const defaultTarget = 'javascript'; const defaultClient = 'fetch'; -// Map target key to editor language for syntax highlighting -function getEditorLanguage(targetKey: string): 'javascript' | 'json' | 'text' { - if (['javascript', 'node'].includes(targetKey)) return 'javascript'; - if (targetKey === 'json') return 'json'; - return 'text'; +// Map httpsnippet target key to editor language for syntax highlighting +const editorLanguageMap: Record = { + c: 'c', + clojure: 'clojure', + csharp: 'csharp', + go: 'go', + http: 'text', + java: 'java', + javascript: 'javascript', + json: 'json', + kotlin: 'kotlin', + node: 'javascript', + objc: 'objective_c', + ocaml: 'ocaml', + php: 'php', + powershell: 'powershell', + python: 'python', + r: 'r', + ruby: 'ruby', + shell: 'shell', + swift: 'swift', +}; + +function getEditorLanguage(targetKey: string): string { + return editorLanguageMap[targetKey] ?? 'text'; } // Convert Yaak HttpRequest to HAR format @@ -159,7 +192,7 @@ export const plugin: PluginDefinition = { }); // Convert to HAR format - const harRequest = toHarRequest(renderedRequest); + const harRequest = toHarRequest(renderedRequest) as HarRequest; // Get previously selected language or use defaults const storedTarget = await ctx.store.get('selectedTarget'); @@ -169,12 +202,15 @@ export const plugin: PluginDefinition = { // Create snippet generator const snippet = new HTTPSnippet(harRequest); + const generateSnippet = (target: string, client: string): string => { + const result = snippet.convert(target as any, client); + return (Array.isArray(result) ? result.join('\n') : result || '').replace(/\r\n/g, '\n'); + }; // Generate initial code preview let initialCode = ''; try { - const result = snippet.convert(initialTarget as any, initialClient); - initialCode = Array.isArray(result) ? result.join('\n') : result || ''; + initialCode = generateSnippet(initialTarget, initialClient); } catch { initialCode = '// Error generating snippet'; } @@ -230,8 +266,7 @@ export const plugin: PluginDefinition = { ); let code: string; try { - const result = snippet.convert(targetKey as any, clientKey); - code = Array.isArray(result) ? result.join('\n') : result || ''; + code = generateSnippet(targetKey, clientKey); } catch { code = '// Error generating snippet'; } @@ -255,8 +290,7 @@ export const plugin: PluginDefinition = { // Generate snippet for the selected language try { - const code = snippet.convert(selectedTarget as any, selectedClient); - const codeText = Array.isArray(code) ? code.join('\n') : code || ''; + const codeText = generateSnippet(selectedTarget, selectedClient); await ctx.clipboard.copyText(codeText); await ctx.toast.show({ message: 'Code snippet copied to clipboard', diff --git a/src-web/components/core/Editor/extensions.ts b/src-web/components/core/Editor/extensions.ts index b9593e9a..9d582ed9 100644 --- a/src-web/components/core/Editor/extensions.ts +++ b/src-web/components/core/Editor/extensions.ts @@ -5,11 +5,14 @@ import { completionKeymap, } from '@codemirror/autocomplete'; import { history, historyKeymap } from '@codemirror/commands'; +import { go } from '@codemirror/lang-go'; +import { java } from '@codemirror/lang-java'; import { javascript } from '@codemirror/lang-javascript'; import { json } from '@codemirror/lang-json'; import { markdown } from '@codemirror/lang-markdown'; +import { php } from '@codemirror/lang-php'; +import { python } from '@codemirror/lang-python'; import { xml } from '@codemirror/lang-xml'; -import type { LanguageSupport } from '@codemirror/language'; import { bracketMatching, codeFolding, @@ -17,8 +20,18 @@ import { foldKeymap, HighlightStyle, indentOnInput, + LanguageSupport, + StreamLanguage, syntaxHighlighting, } from '@codemirror/language'; +import { c, csharp, kotlin, objectiveC } from '@codemirror/legacy-modes/mode/clike'; +import { clojure } from '@codemirror/legacy-modes/mode/clojure'; +import { oCaml } from '@codemirror/legacy-modes/mode/mllike'; +import { powerShell } from '@codemirror/legacy-modes/mode/powershell'; +import { r } from '@codemirror/legacy-modes/mode/r'; +import { ruby } from '@codemirror/legacy-modes/mode/ruby'; +import { shell } from '@codemirror/legacy-modes/mode/shell'; +import { swift } from '@codemirror/legacy-modes/mode/swift'; import { linter, lintGutter, lintKeymap } from '@codemirror/lint'; import { search, searchKeymap } from '@codemirror/search'; @@ -83,6 +96,10 @@ const syntaxTheme = EditorView.theme({}, { dark: true }); const closeBracketsExtensions: Extension = [closeBrackets(), keymap.of([...closeBracketsKeymap])]; +const legacyLang = (mode: Parameters[0]) => { + return () => new LanguageSupport(StreamLanguage.define(mode)); +}; + const syntaxExtensions: Record< NonNullable, null | (() => LanguageSupport) @@ -98,6 +115,21 @@ const syntaxExtensions: Record< text: text, timeline: timeline, markdown: markdown, + c: legacyLang(c), + clojure: legacyLang(clojure), + csharp: legacyLang(csharp), + go: go, + java: java, + kotlin: legacyLang(kotlin), + objective_c: legacyLang(objectiveC), + ocaml: legacyLang(oCaml), + php: php, + powershell: legacyLang(powerShell), + python: python, + r: legacyLang(r), + ruby: legacyLang(ruby), + shell: legacyLang(shell), + swift: legacyLang(swift), }; const closeBracketsFor: (keyof typeof syntaxExtensions)[] = ['json', 'javascript', 'graphql'];