Files
windmill/frontend/src/lib/components/HighlightCode.svelte
T
Ruben Fiszel a46aa641f9 feat: add R language support (#8263)
* feat: add R language support

Add R as a new supported scripting language in Windmill, following the
same pattern used for Ruby. Includes:

- Backend: ScriptLang::Rlang enum variant, DB migration, tree-sitter-r
  parser crate with tests, WASM parser binding, R executor with NSJail
  sandboxing, job dispatch and signature parsing
- Frontend: language picker, R icon, syntax highlighting, editor bar
  insertions (Sys.getenv, get_variable, get_resource), schema inference,
  init code template, BETA badge
- CLI: .r extension mapping, sync support, bootstrap template

R scripts use `main <- function(...)` syntax, jsonlite for JSON
serialization, and system curl for the Windmill client helper.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add R package resolution and installation

Parse library()/require() calls from R scripts to extract dependencies.
Resolve versions from CRAN, cache lockfiles in pip_resolution_cache,
and install packages to a shared R library cache. The run step sets
R_LIBS_USER so installed packages are available to the script.

- Parser: parse_r_requirements() extracts package names from AST
- Executor: resolve() generates lockfile, install() installs from CRAN
- Worker lockfiles: wire up R resolve for dependency jobs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add nsjail sandboxing for R resolve and install phases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: fix R get_variable/get_resource and add sandbox annotation + e2e tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: fix R arg inference with JS fallback parser and get_variable/get_resource

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix flake

* nsjail

* nits

* fix: R install improvements - suppress verbose output, flat lockfile logging, Dockerfile R support, rlimits

- Suppress renv verbose output during resolve and install (controlled by #verbose annotation)
- Filter renv from install list (already loaded, causes noisy restart message)
- Log compact "resolved N packages" instead of full renv.lock JSON
- Add R (r-base, r-cran-renv) to DockerfileFull and DockerfileFullEe
- Use disable_rl for nsjail install config (R compiles from source)
- Reduce default concurrency from 20 to 5
- Add rlang to openflow.openapi.yaml
- Fix MainArgSignature (no_main_func -> auto_kind) after main merge

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* final

* fix: remove accidental R install from multiplayer Dockerfile

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: remove R from Windows build and DockerfileExtra

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: rename R migration to avoid timestamp collision with trigger_filter_logic

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* all

* fix: R install improvements - suppress verbose output, flat lockfile logging, Dockerfile R support, rlimits

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: add clear error when Rscript binary is missing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: fix type errors in R fallback parser, use format! in wrap(), add R system prompts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: pyranota <pyra@duck.com>
2026-04-01 06:11:37 +00:00

151 lines
3.7 KiB
Svelte

<script lang="ts">
import Highlight, { LineNumbers } from 'svelte-highlight'
import python from 'svelte-highlight/languages/python'
import typescript from 'svelte-highlight/languages/typescript'
import go from 'svelte-highlight/languages/go'
import shell from 'svelte-highlight/languages/shell'
import graphql from 'svelte-highlight/languages/graphql'
import javascript from 'svelte-highlight/languages/javascript'
import sql from 'svelte-highlight/languages/sql'
import powershell from 'svelte-highlight/languages/powershell'
import php from 'svelte-highlight/languages/php'
import rust from 'svelte-highlight/languages/rust'
import csharp from 'svelte-highlight/languages/csharp'
import yaml from 'svelte-highlight/languages/yaml'
import java from 'svelte-highlight/languages/java'
import ruby from 'svelte-highlight/languages/ruby'
import r from 'svelte-highlight/languages/r'
import type { Script } from '$lib/gen'
import { Button } from './common'
import { copyToClipboard } from '$lib/utils'
import { ClipboardCopy } from 'lucide-svelte'
import HighlightTheme from './HighlightTheme.svelte'
import { json, type LanguageType } from 'svelte-highlight/languages'
interface Props {
code?: string
language: Script['language'] | 'bunnative' | 'frontend' | 'json' | undefined
highlightLanguage?: LanguageType<string> | undefined
lines?: boolean
className?: string
onApplyCode?: () => void
showApplyButton?: boolean
applyButtonIcon?: typeof ClipboardCopy
}
let {
code = '',
language,
highlightLanguage = undefined,
lines = false,
className = '',
onApplyCode = undefined,
showApplyButton = false,
applyButtonIcon = undefined
}: Props = $props()
function getLang(lang: Script['language'] | 'bunnative' | 'frontend' | 'json' | undefined) {
switch (lang) {
case 'python3':
return python
case 'deno':
return typescript
case 'nativets':
return typescript
case 'bun':
return typescript
case 'bunnative':
return typescript
case 'go':
return go
case 'bash':
return shell
case 'frontend':
return javascript
case 'graphql':
return graphql
case 'mysql':
return sql
case 'postgresql':
return sql
case 'snowflake':
return sql
case 'bigquery':
return sql
case 'oracledb':
return sql
case 'duckdb':
return sql
case 'powershell':
return powershell
case 'php':
return php
case 'rust':
return rust
case 'csharp':
return csharp
case 'nu':
return python
case 'ansible':
return yaml
case 'java':
return java
case 'ruby':
return ruby
case 'rlang':
return r
case 'json':
return json
// for related places search: ADD_NEW_LANG
default:
return typescript
}
}
const lang = $derived(highlightLanguage ?? getLang(language))
</script>
<HighlightTheme />
<div class="relative">
<Button
wrapperClasses="absolute top-2 right-2 z-20"
onclick={() => copyToClipboard(code)}
color="light"
size="xs2"
startIcon={{
icon: ClipboardCopy
}}
iconOnly
title="Copy to clipboard"
/>
{#if showApplyButton}
<Button
wrapperClasses="absolute top-2 right-10 z-20"
onclick={onApplyCode}
color="light"
size="xs2"
startIcon={{
icon: applyButtonIcon
}}
iconOnly
title="Apply code"
/>
{/if}
<div class="overflow-x-auto">
{#if code?.length < 10000}
{#if !lines}
<Highlight class="nowrap {className}" language={lang} {code} />
{:else}
<Highlight class="nowrap {className}" language={lang} {code} let:highlighted>
<LineNumbers {highlighted} />
</Highlight>
{/if}
{:else}
<pre class="overflow-auto max-h-screen text-xs {className}"
><code class="language-{language}">{code}</code></pre
>
{/if}
</div>
</div>