AI: attempt to add autocomplete

This commit is contained in:
mcDandy
2026-06-10 15:58:46 +02:00
parent 6987878ef0
commit 76e14d6f2a
6 changed files with 1072 additions and 58 deletions
+166 -24
View File
@@ -1,12 +1,18 @@
Write-Host "===================================================" -ForegroundColor Cyan
Write-Host "1/2 Compiling ANTLR Grammars" -ForegroundColor Cyan
Write-Host "===================================================" -ForegroundColor Cyan
param(
[switch]$SkipAntlr
)
Write-Host "Compiling legacy version (ANTLR 4.9.3)..." -ForegroundColor Yellow
antlr4 -v 4.9.3 -Dlanguage=Python3 -visitor -o ./legacy MathExpr.g4
if (-not $SkipAntlr) {
Write-Host "===================================================" -ForegroundColor Cyan
Write-Host "1/2 Compiling ANTLR Grammars" -ForegroundColor Cyan
Write-Host "===================================================" -ForegroundColor Cyan
Write-Host "Compiling modern version (ANTLR 4.13.2)..." -ForegroundColor Yellow
antlr4 -v 4.13.2 -Dlanguage=Python3 -visitor -o ./grammer MathExpr.g4
Write-Host "Compiling legacy version (ANTLR 4.9.3)..." -ForegroundColor Yellow
antlr4 -v 4.9.3 -Dlanguage=Python3 -visitor -o ./legacy MathExpr.g4
Write-Host "Compiling modern version (ANTLR 4.13.2)..." -ForegroundColor Yellow
antlr4 -v 4.13.2 -Dlanguage=Python3 -visitor -o ./grammer MathExpr.g4
}
Write-Host ""
Write-Host "===================================================" -ForegroundColor Cyan
@@ -19,12 +25,107 @@ if (-not (Test-Path $g4Path)) {
exit 1
}
# 1. Načtení a očištění gramatiky od komentářů
$content = [System.IO.File]::ReadAllText($g4Path, [System.Text.Encoding]::UTF8)
$content = $content -replace '//.*', ''
$rawContent = [System.IO.File]::ReadAllText($g4Path, [System.Text.Encoding]::UTF8)
$content = $rawContent -replace '//.*', ''
$content = $content -replace '/\*[\s\S]*?\*/', ''
# 2. Vytažení všech alfabetických řetězců v apostrofech
function Get-LexerNames([string]$grammarText) {
$map = @{}
foreach ($line in ($grammarText -split "`r?`n")) {
$t = $line.Trim()
if ($t -notmatch '^([A-Z][A-Z0-9_]*)\s*:\s*(.+);\s*$') { continue }
$token = $matches[1]
$rhs = $matches[2]
$names = @(
[regex]::Matches($rhs, "'([^']*)'") | ForEach-Object { $_.Groups[1].Value.ToLower() }
) | Sort-Object -Unique
if ($names.Count -gt 0) {
$map[$token] = $names
}
}
return $map
}
function Get-ArgBounds([string]$inner) {
$inner = $inner.Trim()
if ([string]::IsNullOrWhiteSpace($inner)) {
return @{ min = 0; max = 0 }
}
$exprCount = ([regex]::Matches($inner, '\b(expr|indexExpr)\b')).Count
$optExpr = 0
foreach ($g in [regex]::Matches($inner, '\([^()]*\)\?')) {
$optExpr += ([regex]::Matches($g.Value, '\b(expr|indexExpr)\b')).Count
}
$hasStar = $inner -match '\(COMMA (expr|indexExpr)\)\*'
$hasPlus = $inner -match '\(COMMA (expr|indexExpr)\)\+'
$min = $exprCount - $optExpr
if ($hasStar) {
$min = [Math]::Max(1, $exprCount - 1 - $optExpr)
return @{ min = $min; max = $null }
}
if ($hasPlus) {
$min = [Math]::Max(1, $exprCount - $optExpr)
return @{ min = $min; max = $null }
}
return @{ min = $min; max = $exprCount }
}
function New-Snippet([string]$name) {
return "${name}()"
}
function Add-FunctionRuleLine([hashtable]$meta, [hashtable]$lexerMap, [string]$line) {
$clean = (($line -split '\s+#')[0]).Trim().TrimEnd(';')
if ($clean -notmatch 'LPAREN') { return }
if ($clean -notmatch '(?:^\s*\|\s*)?(\w+)\s+LPAREN\s*(.*?)\s+RPAREN\s*$') { return }
$token = $matches[1]
$inner = $matches[2].Trim()
$bounds = Get-ArgBounds $inner
$names = $lexerMap[$token]
if (-not $names) { return }
foreach ($fn in $names) {
$entry = @{
minArgs = $bounds.min
snippet = (New-Snippet $fn)
}
if ($null -eq $bounds.max) {
$entry.maxArgs = $null
} else {
$entry.maxArgs = $bounds.max
}
$meta[$fn] = $entry
}
}
function Get-FunctionMeta([string]$grammarText, [hashtable]$lexerMap) {
$meta = @{}
$inFunc = $false
foreach ($line in ($grammarText -split "`r?`n")) {
if ($line -match '^\s*//\s*LEXER') { $inFunc = $false }
if ($line -match '^\s*(func0|func1|func2|func3|func4|func5|funcN|funcNoise)\s*:\s*(.*)$') {
$inFunc = $true
$inline = $matches[2].Trim()
if ($inline -and $inline -match 'LPAREN') {
Add-FunctionRuleLine $meta $lexerMap $inline
}
continue
}
if (-not $inFunc) { continue }
Add-FunctionRuleLine $meta $lexerMap $line
}
return $meta
}
# 1. Extract quoted symbols for keyword/function lists
$matches = [regex]::Matches($content, "'([a-zA-Z_][a-zA-Z0-9_]*)'")
$allSymbols = @()
foreach ($m in $matches) {
@@ -32,51 +133,92 @@ foreach ($m in $matches) {
}
$allSymbols = $allSymbols | Sort-Object -Unique
# 3. Definice pevných množin
# 2. Fixed sets
$keywords = @('if', 'else', 'while', 'for', 'in', 'break', 'continue', 'return', 'none', 'None', 'null', 'NULL') | Sort-Object -Unique
$constants = @('pi', 'PI', 'e', 'E') | Sort-Object -Unique
# Vše ostatní, co jsou slova a nejsou klíčová slova ani konstanty, jsou funkce
$functions = @()
foreach ($sym in $allSymbols) {
if ($sym -notin $keywords -and $sym -notin $constants) {
$functions += $sym
$functions += $sym.ToLower()
}
}
$functions = $functions | Sort-Object -Unique
# 4. Formátování a zápis do PYTHON souboru (do kořene/aktuální složky vedle .g4)
# 3. Function metadata from grammar rules
$lexerMap = Get-LexerNames $content
$functionMeta = Get-FunctionMeta $content $lexerMap
# Ensure every listed function has meta (fallback: variadic unknown)
foreach ($fn in $functions) {
if (-not $functionMeta.ContainsKey($fn)) {
$functionMeta[$fn] = @{
minArgs = 1
maxArgs = $null
snippet = (New-Snippet $fn)
}
}
}
$sortedMetaKeys = $functionMeta.Keys | Sort-Object
# 4. Python export
$pyMetaLines = @()
foreach ($key in $sortedMetaKeys) {
$m = $functionMeta[$key]
$maxPart = if ($null -eq $m.maxArgs) { "None" } else { [string]$m.maxArgs }
$pyMetaLines += " '$key': {'min_args': $($m.minArgs), 'max_args': $maxPart, 'snippet': '$($m.snippet)'},"
}
$pyPath = "inbuilt_symbols.py"
$pyContent = @(
"# Generated automatically by compile.ps1. Do not edit.",
"INBUILT_KEYWORDS = {" + (($keywords | foreach { "'$_'" }) -join ", ") + "}",
"INBUILT_CONSTANTS = {" + (($constants | foreach { "'$_'" }) -join ", ") + "}",
"INBUILT_FUNCTIONS = {" + (($functions | foreach { "'$_'" }) -join ", ") + "}"
"INBUILT_KEYWORDS = {" + (($keywords | ForEach-Object { "'$_'" }) -join ", ") + "}",
"INBUILT_CONSTANTS = {" + (($constants | ForEach-Object { "'$_'" }) -join ", ") + "}",
"INBUILT_FUNCTIONS = {" + (($functions | ForEach-Object { "'$_'" }) -join ", ") + "}",
"",
"INBUILT_FUNCTION_META = {",
($pyMetaLines -join [Environment]::NewLine),
"}"
) -join [Environment]::NewLine
[System.IO.File]::WriteAllText($pyPath, $pyContent, [System.Text.Encoding]::UTF8)
Write-Host "-> Exported Python symbols to root: $pyPath" -ForegroundColor Green
# 5. Formátování a zápis do JAVASCRIPT souboru (odskok do složky web)
# Pokud máš složku web hned vedle .g4, změň '..\web' na 'web'
$jsDir = "..\..\web"
# 5. JavaScript export
$jsDir = "..\..\web"
if (-not (Test-Path $jsDir)) {
New-Item -ItemType Directory -Force -Path $jsDir | Out-Null
}
$jsPath = Join-Path $jsDir "inbuilt_symbols.js"
$jsMetaLines = @()
foreach ($key in $sortedMetaKeys) {
$m = $functionMeta[$key]
if ($null -eq $m.maxArgs) {
$jsMetaLines += " $key`: { minArgs: $($m.minArgs), maxArgs: null, snippet: `"$($m.snippet)`" },"
} else {
$jsMetaLines += " $key`: { minArgs: $($m.minArgs), maxArgs: $($m.maxArgs), snippet: `"$($m.snippet)`" },"
}
}
$jsContent = @(
"// Generated automatically by compile.ps1. Do not edit.",
"",
"export const KEYWORDS = new Set([" + (($keywords | foreach { "`"$_`"" }) -join ", ") + "]);",
"export const KEYWORDS = new Set([" + (($keywords | ForEach-Object { "`"$_`"" }) -join ", ") + "]);",
"",
"export const CONSTANTS = new Set([" + (($constants | foreach { "`"$_`"" }) -join ", ") + "]);",
"export const CONSTANTS = new Set([" + (($constants | ForEach-Object { "`"$_`"" }) -join ", ") + "]);",
"",
"export const FUNCTIONS = new Set([" + (($functions | foreach { "`"$_`"" }) -join ", ") + "]);"
"export const FUNCTIONS = new Set([" + (($functions | ForEach-Object { "`"$_`"" }) -join ", ") + "]);",
"",
"export const FUNCTION_META = {",
($jsMetaLines -join [Environment]::NewLine),
"};"
) -join [Environment]::NewLine
[System.IO.File]::WriteAllText($jsPath, $jsContent, [System.Text.Encoding]::UTF8)
Write-Host "-> Exported JS symbols to: $jsPath" -ForegroundColor Green
Write-Host "-> Function metadata entries: $($sortedMetaKeys.Count)" -ForegroundColor Green
Write-Host ""
Write-Host "===================================================" -ForegroundColor Cyan
+237
View File
@@ -7,4 +7,241 @@ INBUILT_CONSTANTS = {
}
INBUILT_FUNCTIONS = {
'abs', 'acos', 'acosh', 'all', 'angle', 'any', 'append', 'argmax', 'argmin', 'argsort', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'band', 'batch_shuffle', 'bitcount', 'bitwise_and', 'bitwise_not', 'bitwise_or', 'bitwise_xor', 'blur', 'bnot', 'bor', 'botk', 'botk_ind', 'botk_indices', 'bxor', 'cat', 'ceil', 'cellular', 'cellular_noise', 'cielab_to_rgb', 'clamp', 'cnt', 'concat', 'concatenate', 'conv', 'convolution', 'corr', 'correlation', 'cos', 'cosh', 'cosine_similarity', 'cossim', 'count', 'cov', 'crop', 'cross', 'cubic', 'cubic_ease', 'cumprod', 'cumsum', 'dilate', 'dist', 'distance', 'domain_warp', 'domain_warp_noise', 'dot', 'edge', 'elastic', 'elastic_ease', 'entropy', 'erf', 'erfinv', 'erode', 'exp', 'ezconv', 'ezconvolution', 'fft', 'find', 'flatten', 'flip', 'float', 'floor', 'flow_ang', 'flow_angle', 'flow_apply', 'flow_mag', 'flow_magnitude', 'flow_to_image', 'fract', 'gamma', 'gaussian', 'gelu', 'get_value', 'hist', 'histogram', 'hsv_to_rgb', 'ifft', 'int', 'int_to_rgb', 'interpolate_area', 'interpolate_linear', 'interpolate_nearest', 'interpolate_nearest_exact', 'join', 'length', 'lerp', 'linspace', 'ln', 'log', 'logspace', 'lower', 'map', 'matmul', 'mean', 'median', 'mode', 'moment', 'morph_close', 'morph_open', 'motion_mask', 'nan_to_num', 'noise', 'now', 'nvl', 'oklab_to_rgb', 'overlay', 'pad', 'percentile', 'perlin', 'perlin_noise', 'perm', 'permute', 'pinv', 'plasma', 'plasma_noise', 'popcnt', 'popcount', 'pow', 'prcnt', 'print', 'print_shape', 'pshp', 'quantile', 'quartil', 'quartile', 'rand', 'randb', 'randbeta', 'randc', 'rande', 'randg', 'randgumbel', 'randchi2', 'randl', 'randln', 'randn', 'random_bernoulli', 'random_beta', 'random_cauchy', 'random_exponential', 'random_gamma', 'random_gumbel', 'random_chi2', 'random_laplace', 'random_log_normal', 'random_normal', 'random_poisson', 'random_studentt', 'random_uniform', 'random_weibull', 'randp', 'randt', 'randu', 'randw', 'range', 'relu', 'remap', 'replace', 'reshape', 'rgb_to_cielab', 'rgb_to_hsv', 'rgb_to_int', 'rgb_to_oklab', 'ridged', 'ridged_noise', 'rife', 'roll', 'round', 'rshp', 'select', 'shape', 'shuffle', 'sigm', 'sign', 'sin', 'sine', 'sine_ease', 'sinh', 'smax', 'smin', 'smootherstep', 'smoothstep', 'snorm', 'softmax', 'softmin', 'softplus', 'sort', 'split', 'sqrt', 'stack_clear', 'stack_get', 'stack_has', 'stack_pop', 'stack_push', 'std', 'step', 'substr', 'substring', 'sum', 'swap', 'tan', 'tanh', 'tensor', 'text_image', 'timestamp', 'tmax', 'tmin', 'tnorm', 'topk', 'topk_ind', 'topk_indices', 'trim', 'turbulence', 'unique', 'upper', 'var', 'voronoi', 'voronoi_noise', 'where', 'worley'
}
INBUILT_FUNCTION_META = {
'abs': {'min_args': 1, 'max_args': 1, 'snippet': 'abs()'},
'acos': {'min_args': 1, 'max_args': 1, 'snippet': 'acos()'},
'acosh': {'min_args': 1, 'max_args': 1, 'snippet': 'acosh()'},
'all': {'min_args': 1, 'max_args': 1, 'snippet': 'all()'},
'angle': {'min_args': 1, 'max_args': 1, 'snippet': 'angle()'},
'any': {'min_args': 1, 'max_args': 1, 'snippet': 'any()'},
'append': {'min_args': 2, 'max_args': 2, 'snippet': 'append()'},
'argmax': {'min_args': 1, 'max_args': 1, 'snippet': 'argmax()'},
'argmin': {'min_args': 1, 'max_args': 1, 'snippet': 'argmin()'},
'argsort': {'min_args': 1, 'max_args': 2, 'snippet': 'argsort()'},
'asin': {'min_args': 1, 'max_args': 1, 'snippet': 'asin()'},
'asinh': {'min_args': 1, 'max_args': 1, 'snippet': 'asinh()'},
'atan': {'min_args': 1, 'max_args': 1, 'snippet': 'atan()'},
'atan2': {'min_args': 2, 'max_args': 2, 'snippet': 'atan2()'},
'atanh': {'min_args': 1, 'max_args': 1, 'snippet': 'atanh()'},
'band': {'min_args': 2, 'max_args': 2, 'snippet': 'band()'},
'batch_shuffle': {'min_args': 2, 'max_args': 2, 'snippet': 'batch_shuffle()'},
'bitcount': {'min_args': 1, 'max_args': 1, 'snippet': 'bitcount()'},
'bitwise_and': {'min_args': 2, 'max_args': 2, 'snippet': 'bitwise_and()'},
'bitwise_not': {'min_args': 1, 'max_args': 1, 'snippet': 'bitwise_not()'},
'bitwise_or': {'min_args': 2, 'max_args': 2, 'snippet': 'bitwise_or()'},
'bitwise_xor': {'min_args': 2, 'max_args': 2, 'snippet': 'bitwise_xor()'},
'blur': {'min_args': 2, 'max_args': 3, 'snippet': 'blur()'},
'bnot': {'min_args': 1, 'max_args': 1, 'snippet': 'bnot()'},
'bor': {'min_args': 2, 'max_args': 2, 'snippet': 'bor()'},
'botk': {'min_args': 2, 'max_args': 2, 'snippet': 'botk()'},
'botk_ind': {'min_args': 2, 'max_args': 2, 'snippet': 'botk_ind()'},
'botk_indices': {'min_args': 2, 'max_args': 2, 'snippet': 'botk_indices()'},
'bxor': {'min_args': 2, 'max_args': 2, 'snippet': 'bxor()'},
'cat': {'min_args': 2, 'max_args': None, 'snippet': 'cat()'},
'ceil': {'min_args': 1, 'max_args': 1, 'snippet': 'ceil()'},
'cellular': {'min_args': 2, 'max_args': 5, 'snippet': 'cellular()'},
'cellular_noise': {'min_args': 2, 'max_args': 5, 'snippet': 'cellular_noise()'},
'cielab_to_rgb': {'min_args': 1, 'max_args': 3, 'snippet': 'cielab_to_rgb()'},
'clamp': {'min_args': 3, 'max_args': 3, 'snippet': 'clamp()'},
'cnt': {'min_args': 1, 'max_args': 1, 'snippet': 'cnt()'},
'concat': {'min_args': 2, 'max_args': None, 'snippet': 'concat()'},
'concatenate': {'min_args': 2, 'max_args': None, 'snippet': 'concatenate()'},
'conv': {'min_args': 2, 'max_args': None, 'snippet': 'conv()'},
'convolution': {'min_args': 2, 'max_args': None, 'snippet': 'convolution()'},
'corr': {'min_args': 2, 'max_args': 2, 'snippet': 'corr()'},
'correlation': {'min_args': 2, 'max_args': 2, 'snippet': 'correlation()'},
'cos': {'min_args': 1, 'max_args': 1, 'snippet': 'cos()'},
'cosh': {'min_args': 1, 'max_args': 1, 'snippet': 'cosh()'},
'cosine_similarity': {'min_args': 2, 'max_args': 2, 'snippet': 'cosine_similarity()'},
'cossim': {'min_args': 2, 'max_args': 2, 'snippet': 'cossim()'},
'count': {'min_args': 1, 'max_args': 1, 'snippet': 'count()'},
'cov': {'min_args': 2, 'max_args': 2, 'snippet': 'cov()'},
'crop': {'min_args': 3, 'max_args': 3, 'snippet': 'crop()'},
'cross': {'min_args': 2, 'max_args': 2, 'snippet': 'cross()'},
'cubic': {'min_args': 3, 'max_args': 3, 'snippet': 'cubic()'},
'cubic_ease': {'min_args': 3, 'max_args': 3, 'snippet': 'cubic_ease()'},
'cumprod': {'min_args': 1, 'max_args': 1, 'snippet': 'cumprod()'},
'cumsum': {'min_args': 1, 'max_args': 1, 'snippet': 'cumsum()'},
'dilate': {'min_args': 1, 'max_args': 2, 'snippet': 'dilate()'},
'dist': {'min_args': 4, 'max_args': 4, 'snippet': 'dist()'},
'distance': {'min_args': 4, 'max_args': 4, 'snippet': 'distance()'},
'domain_warp': {'min_args': 4, 'max_args': 8, 'snippet': 'domain_warp()'},
'domain_warp_noise': {'min_args': 4, 'max_args': 8, 'snippet': 'domain_warp_noise()'},
'dot': {'min_args': 2, 'max_args': 2, 'snippet': 'dot()'},
'edge': {'min_args': 1, 'max_args': 2, 'snippet': 'edge()'},
'elastic': {'min_args': 3, 'max_args': 3, 'snippet': 'elastic()'},
'elastic_ease': {'min_args': 3, 'max_args': 3, 'snippet': 'elastic_ease()'},
'entropy': {'min_args': 1, 'max_args': 1, 'snippet': 'entropy()'},
'erf': {'min_args': 1, 'max_args': 1, 'snippet': 'erf()'},
'erfinv': {'min_args': 1, 'max_args': 1, 'snippet': 'erfinv()'},
'erode': {'min_args': 1, 'max_args': 2, 'snippet': 'erode()'},
'exp': {'min_args': 1, 'max_args': 1, 'snippet': 'exp()'},
'ezconv': {'min_args': 2, 'max_args': None, 'snippet': 'ezconv()'},
'ezconvolution': {'min_args': 2, 'max_args': None, 'snippet': 'ezconvolution()'},
'fft': {'min_args': 1, 'max_args': 1, 'snippet': 'fft()'},
'find': {'min_args': 2, 'max_args': 2, 'snippet': 'find()'},
'flatten': {'min_args': 1, 'max_args': 1, 'snippet': 'flatten()'},
'flip': {'min_args': 2, 'max_args': 2, 'snippet': 'flip()'},
'float': {'min_args': 1, 'max_args': 1, 'snippet': 'float()'},
'floor': {'min_args': 1, 'max_args': 1, 'snippet': 'floor()'},
'flow_ang': {'min_args': 1, 'max_args': 1, 'snippet': 'flow_ang()'},
'flow_angle': {'min_args': 1, 'max_args': 1, 'snippet': 'flow_angle()'},
'flow_apply': {'min_args': 2, 'max_args': 2, 'snippet': 'flow_apply()'},
'flow_mag': {'min_args': 1, 'max_args': 1, 'snippet': 'flow_mag()'},
'flow_magnitude': {'min_args': 1, 'max_args': 1, 'snippet': 'flow_magnitude()'},
'flow_to_image': {'min_args': 1, 'max_args': 1, 'snippet': 'flow_to_image()'},
'fract': {'min_args': 1, 'max_args': 1, 'snippet': 'fract()'},
'gamma': {'min_args': 1, 'max_args': 1, 'snippet': 'gamma()'},
'gaussian': {'min_args': 2, 'max_args': 3, 'snippet': 'gaussian()'},
'gelu': {'min_args': 1, 'max_args': 1, 'snippet': 'gelu()'},
'get_value': {'min_args': 2, 'max_args': 2, 'snippet': 'get_value()'},
'hist': {'min_args': 4, 'max_args': 4, 'snippet': 'hist()'},
'histogram': {'min_args': 4, 'max_args': 4, 'snippet': 'histogram()'},
'hsv_to_rgb': {'min_args': 1, 'max_args': 4, 'snippet': 'hsv_to_rgb()'},
'ifft': {'min_args': 1, 'max_args': 2, 'snippet': 'ifft()'},
'int': {'min_args': 1, 'max_args': 1, 'snippet': 'int()'},
'int_to_rgb': {'min_args': 1, 'max_args': 1, 'snippet': 'int_to_rgb()'},
'interpolate_area': {'min_args': 2, 'max_args': 2, 'snippet': 'interpolate_area()'},
'interpolate_linear': {'min_args': 2, 'max_args': 2, 'snippet': 'interpolate_linear()'},
'interpolate_nearest': {'min_args': 2, 'max_args': 2, 'snippet': 'interpolate_nearest()'},
'interpolate_nearest_exact': {'min_args': 2, 'max_args': 2, 'snippet': 'interpolate_nearest_exact()'},
'join': {'min_args': 1, 'max_args': 2, 'snippet': 'join()'},
'length': {'min_args': 1, 'max_args': 1, 'snippet': 'length()'},
'lerp': {'min_args': 3, 'max_args': 3, 'snippet': 'lerp()'},
'linspace': {'min_args': 3, 'max_args': 3, 'snippet': 'linspace()'},
'ln': {'min_args': 1, 'max_args': 1, 'snippet': 'ln()'},
'log': {'min_args': 1, 'max_args': 1, 'snippet': 'log()'},
'logspace': {'min_args': 4, 'max_args': 4, 'snippet': 'logspace()'},
'lower': {'min_args': 1, 'max_args': 1, 'snippet': 'lower()'},
'map': {'min_args': 2, 'max_args': None, 'snippet': 'map()'},
'matmul': {'min_args': 2, 'max_args': 2, 'snippet': 'matmul()'},
'mean': {'min_args': 1, 'max_args': 1, 'snippet': 'mean()'},
'median': {'min_args': 1, 'max_args': 1, 'snippet': 'median()'},
'mode': {'min_args': 1, 'max_args': 1, 'snippet': 'mode()'},
'moment': {'min_args': 3, 'max_args': 3, 'snippet': 'moment()'},
'morph_close': {'min_args': 1, 'max_args': 2, 'snippet': 'morph_close()'},
'morph_open': {'min_args': 1, 'max_args': 2, 'snippet': 'morph_open()'},
'motion_mask': {'min_args': 1, 'max_args': 1, 'snippet': 'motion_mask()'},
'nan_to_num': {'min_args': 4, 'max_args': 4, 'snippet': 'nan_to_num()'},
'noise': {'min_args': 1, 'max_args': 2, 'snippet': 'noise()'},
'now': {'min_args': 0, 'max_args': 0, 'snippet': 'now()'},
'nvl': {'min_args': 4, 'max_args': 4, 'snippet': 'nvl()'},
'oklab_to_rgb': {'min_args': 1, 'max_args': 3, 'snippet': 'oklab_to_rgb()'},
'overlay': {'min_args': 3, 'max_args': 4, 'snippet': 'overlay()'},
'pad': {'min_args': 2, 'max_args': 2, 'snippet': 'pad()'},
'percentile': {'min_args': 2, 'max_args': 2, 'snippet': 'percentile()'},
'perlin': {'min_args': 2, 'max_args': 5, 'snippet': 'perlin()'},
'perlin_noise': {'min_args': 2, 'max_args': 5, 'snippet': 'perlin_noise()'},
'perm': {'min_args': 2, 'max_args': 2, 'snippet': 'perm()'},
'permute': {'min_args': 2, 'max_args': 2, 'snippet': 'permute()'},
'pinv': {'min_args': 1, 'max_args': 1, 'snippet': 'pinv()'},
'plasma': {'min_args': 2, 'max_args': 5, 'snippet': 'plasma()'},
'plasma_noise': {'min_args': 2, 'max_args': 5, 'snippet': 'plasma_noise()'},
'popcnt': {'min_args': 1, 'max_args': 1, 'snippet': 'popcnt()'},
'popcount': {'min_args': 1, 'max_args': 1, 'snippet': 'popcount()'},
'pow': {'min_args': 2, 'max_args': 2, 'snippet': 'pow()'},
'prcnt': {'min_args': 2, 'max_args': 2, 'snippet': 'prcnt()'},
'print': {'min_args': 1, 'max_args': 1, 'snippet': 'print()'},
'print_shape': {'min_args': 1, 'max_args': 1, 'snippet': 'print_shape()'},
'pshp': {'min_args': 1, 'max_args': 1, 'snippet': 'pshp()'},
'quantile': {'min_args': 2, 'max_args': 2, 'snippet': 'quantile()'},
'quartil': {'min_args': 2, 'max_args': 2, 'snippet': 'quartil()'},
'quartile': {'min_args': 2, 'max_args': 2, 'snippet': 'quartile()'},
'rand': {'min_args': 1, 'max_args': 2, 'snippet': 'rand()'},
'randb': {'min_args': 2, 'max_args': 3, 'snippet': 'randb()'},
'randbeta': {'min_args': 3, 'max_args': 4, 'snippet': 'randbeta()'},
'randc': {'min_args': 3, 'max_args': 4, 'snippet': 'randc()'},
'rande': {'min_args': 2, 'max_args': 3, 'snippet': 'rande()'},
'randg': {'min_args': 3, 'max_args': 4, 'snippet': 'randg()'},
'randgumbel': {'min_args': 3, 'max_args': 4, 'snippet': 'randgumbel()'},
'randchi2': {'min_args': 2, 'max_args': 3, 'snippet': 'randchi2()'},
'randl': {'min_args': 3, 'max_args': 4, 'snippet': 'randl()'},
'randln': {'min_args': 3, 'max_args': 4, 'snippet': 'randln()'},
'randn': {'min_args': 1, 'max_args': 2, 'snippet': 'randn()'},
'random_bernoulli': {'min_args': 2, 'max_args': 3, 'snippet': 'random_bernoulli()'},
'random_beta': {'min_args': 3, 'max_args': 4, 'snippet': 'random_beta()'},
'random_cauchy': {'min_args': 3, 'max_args': 4, 'snippet': 'random_cauchy()'},
'random_exponential': {'min_args': 2, 'max_args': 3, 'snippet': 'random_exponential()'},
'random_gamma': {'min_args': 3, 'max_args': 4, 'snippet': 'random_gamma()'},
'random_gumbel': {'min_args': 3, 'max_args': 4, 'snippet': 'random_gumbel()'},
'random_chi2': {'min_args': 2, 'max_args': 3, 'snippet': 'random_chi2()'},
'random_laplace': {'min_args': 3, 'max_args': 4, 'snippet': 'random_laplace()'},
'random_log_normal': {'min_args': 3, 'max_args': 4, 'snippet': 'random_log_normal()'},
'random_normal': {'min_args': 1, 'max_args': 2, 'snippet': 'random_normal()'},
'random_poisson': {'min_args': 2, 'max_args': 3, 'snippet': 'random_poisson()'},
'random_studentt': {'min_args': 2, 'max_args': 3, 'snippet': 'random_studentt()'},
'random_uniform': {'min_args': 1, 'max_args': 2, 'snippet': 'random_uniform()'},
'random_weibull': {'min_args': 3, 'max_args': 4, 'snippet': 'random_weibull()'},
'randp': {'min_args': 2, 'max_args': 3, 'snippet': 'randp()'},
'randt': {'min_args': 2, 'max_args': 3, 'snippet': 'randt()'},
'randu': {'min_args': 1, 'max_args': 2, 'snippet': 'randu()'},
'randw': {'min_args': 3, 'max_args': 4, 'snippet': 'randw()'},
'range': {'min_args': 3, 'max_args': 3, 'snippet': 'range()'},
'relu': {'min_args': 1, 'max_args': None, 'snippet': 'relu()'},
'remap': {'min_args': 5, 'max_args': 5, 'snippet': 'remap()'},
'replace': {'min_args': 3, 'max_args': 3, 'snippet': 'replace()'},
'reshape': {'min_args': 2, 'max_args': 2, 'snippet': 'reshape()'},
'rgb_to_cielab': {'min_args': 1, 'max_args': 3, 'snippet': 'rgb_to_cielab()'},
'rgb_to_hsv': {'min_args': 1, 'max_args': 4, 'snippet': 'rgb_to_hsv()'},
'rgb_to_int': {'min_args': 1, 'max_args': 3, 'snippet': 'rgb_to_int()'},
'rgb_to_oklab': {'min_args': 1, 'max_args': 3, 'snippet': 'rgb_to_oklab()'},
'ridged': {'min_args': 2, 'max_args': 5, 'snippet': 'ridged()'},
'ridged_noise': {'min_args': 2, 'max_args': 5, 'snippet': 'ridged_noise()'},
'rife': {'min_args': 4, 'max_args': 5, 'snippet': 'rife()'},
'roll': {'min_args': 2, 'max_args': 3, 'snippet': 'roll()'},
'round': {'min_args': 1, 'max_args': 1, 'snippet': 'round()'},
'rshp': {'min_args': 2, 'max_args': 2, 'snippet': 'rshp()'},
'select': {'min_args': 2, 'max_args': 2, 'snippet': 'select()'},
'shape': {'min_args': 1, 'max_args': 1, 'snippet': 'shape()'},
'shuffle': {'min_args': 2, 'max_args': 2, 'snippet': 'shuffle()'},
'sigm': {'min_args': 1, 'max_args': 1, 'snippet': 'sigm()'},
'sign': {'min_args': 1, 'max_args': 1, 'snippet': 'sign()'},
'sin': {'min_args': 1, 'max_args': 1, 'snippet': 'sin()'},
'sine': {'min_args': 3, 'max_args': 3, 'snippet': 'sine()'},
'sine_ease': {'min_args': 3, 'max_args': 3, 'snippet': 'sine_ease()'},
'sinh': {'min_args': 1, 'max_args': 1, 'snippet': 'sinh()'},
'smax': {'min_args': 1, 'max_args': None, 'snippet': 'smax()'},
'smin': {'min_args': 1, 'max_args': None, 'snippet': 'smin()'},
'smootherstep': {'min_args': 3, 'max_args': 3, 'snippet': 'smootherstep()'},
'smoothstep': {'min_args': 3, 'max_args': 3, 'snippet': 'smoothstep()'},
'snorm': {'min_args': 1, 'max_args': 1, 'snippet': 'snorm()'},
'softmax': {'min_args': 1, 'max_args': 2, 'snippet': 'softmax()'},
'softmin': {'min_args': 1, 'max_args': 2, 'snippet': 'softmin()'},
'softplus': {'min_args': 1, 'max_args': 1, 'snippet': 'softplus()'},
'sort': {'min_args': 1, 'max_args': 1, 'snippet': 'sort()'},
'split': {'min_args': 1, 'max_args': 2, 'snippet': 'split()'},
'sqrt': {'min_args': 1, 'max_args': 1, 'snippet': 'sqrt()'},
'stack_clear': {'min_args': 1, 'max_args': 1, 'snippet': 'stack_clear()'},
'stack_get': {'min_args': 1, 'max_args': 1, 'snippet': 'stack_get()'},
'stack_has': {'min_args': 1, 'max_args': 1, 'snippet': 'stack_has()'},
'stack_pop': {'min_args': 1, 'max_args': 1, 'snippet': 'stack_pop()'},
'stack_push': {'min_args': 2, 'max_args': 2, 'snippet': 'stack_push()'},
'std': {'min_args': 1, 'max_args': 1, 'snippet': 'std()'},
'step': {'min_args': 2, 'max_args': 2, 'snippet': 'step()'},
'substr': {'min_args': 2, 'max_args': 3, 'snippet': 'substr()'},
'substring': {'min_args': 2, 'max_args': 3, 'snippet': 'substring()'},
'sum': {'min_args': 1, 'max_args': 1, 'snippet': 'sum()'},
'swap': {'min_args': 4, 'max_args': 4, 'snippet': 'swap()'},
'tan': {'min_args': 1, 'max_args': 1, 'snippet': 'tan()'},
'tanh': {'min_args': 1, 'max_args': 1, 'snippet': 'tanh()'},
'tensor': {'min_args': 2, 'max_args': 3, 'snippet': 'tensor()'},
'text_image': {'min_args': 3, 'max_args': 9, 'snippet': 'text_image()'},
'timestamp': {'min_args': 0, 'max_args': 0, 'snippet': 'timestamp()'},
'tmax': {'min_args': 2, 'max_args': 2, 'snippet': 'tmax()'},
'tmin': {'min_args': 2, 'max_args': 2, 'snippet': 'tmin()'},
'tnorm': {'min_args': 1, 'max_args': 1, 'snippet': 'tnorm()'},
'topk': {'min_args': 2, 'max_args': 2, 'snippet': 'topk()'},
'topk_ind': {'min_args': 2, 'max_args': 2, 'snippet': 'topk_ind()'},
'topk_indices': {'min_args': 2, 'max_args': 2, 'snippet': 'topk_indices()'},
'trim': {'min_args': 1, 'max_args': 1, 'snippet': 'trim()'},
'turbulence': {'min_args': 2, 'max_args': 5, 'snippet': 'turbulence()'},
'unique': {'min_args': 1, 'max_args': 1, 'snippet': 'unique()'},
'upper': {'min_args': 1, 'max_args': 1, 'snippet': 'upper()'},
'var': {'min_args': 1, 'max_args': 1, 'snippet': 'var()'},
'voronoi': {'min_args': 2, 'max_args': 5, 'snippet': 'voronoi()'},
'voronoi_noise': {'min_args': 2, 'max_args': 5, 'snippet': 'voronoi_noise()'},
'where': {'min_args': 3, 'max_args': 3, 'snippet': 'where()'},
'worley': {'min_args': 2, 'max_args': 5, 'snippet': 'worley()'},
}
+11 -33
View File
@@ -1,6 +1,7 @@
<template>
<div
class="mrmth-editor"
data-mrmth-script-widget="vue"
ref="wrapperRef"
:style="editorStyle"
>
@@ -61,6 +62,8 @@
<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
import { KEYWORDS, CONSTANTS, FUNCTIONS } from './inbuilt_symbols.js';
import { attachAutocomplete } from './autocomplete.js';
// Define model for value binding
const modelValue = defineModel<string>({ default: '' });
@@ -100,39 +103,6 @@ const escapeHtml = (value: string): string => {
.replace(/'/g, '&#39;');
};
// Language constructs for tokenizing
const KEYWORDS = new Set(['if', 'else', 'while', 'for', 'in', 'break', 'continue', 'return', 'none', 'None', 'null', 'NULL']);
const CONSTANTS = new Set(['pi', 'PI', 'e', 'E']);
const FUNCTIONS = new Set([
'sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'atan2', 'sinh', 'cosh', 'tanh', 'asinh', 'acosh', 'atanh',
'abs', 'sqrt', 'ln', 'log', 'exp', 'smin', 'smax', 'tmin', 'tmax', 'tnorm', 'snorm', 'floor', 'ceil',
'round', 'gamma', 'pow', 'sigm', 'clamp', 'fft', 'ifft', 'angle', 'print', 'print_shape', 'pshp', 'nvl',
'nan_to_num', 'lerp', 'step', 'smoothstep', 'fract', 'relu', 'softplus', 'gelu', 'sign', 'map', 'ezconvolution',
'ezconv', 'convolution', 'conv', 'swap', 'permute', 'perm', 'reshape', 'rshp', 'range', 'topk', 'botk', 'pinv',
'sum', 'mean', 'std', 'var', 'quartile', 'quartil', 'percentile', 'prcnt', 'quantile', 'dot', 'moment', 'erf', 'erfinv', 'any',
'all', 'edge', 'blur', 'gaussian', 'median', 'mode', 'cumsum', 'cumprod', 'topk_ind', 'topk_indices', 'botk_ind',
'botk_indices', 'cubic_ease', 'cubic', 'elastic_ease', 'elastic', 'sine_ease', 'sine', 'smootherstep', 'dist',
'distance', 'remap', 'cossim', 'cosine_similarity', 'count', 'cnt', 'length', 'flatten', 'append', 'get_value',
'flow_apply', 'batch_shuffle', 'shuffle', 'motion_mask', 'flow_to_image', 'overlay', 'pad', 'cross', 'matmul', 'rife',
'bnot', 'bitwise_not', 'bitcount', 'popcount', 'popcnt', 'shape', 'band', 'bitwise_and', 'bxor', 'bitwise_xor',
'bor', 'bitwise_or', 'tensor', 'stack_push', 'stack_pop', 'stack_clear', 'stack_has', 'stack_get', 'timestamp', 'now',
'sort', 'argsort', 'argmin', 'argmax', 'softmax', 'softmin', 'unique', 'flip', 'cov', 'corr', 'correlation', 'entropy',
'crop', 'cat', 'concatenate', 'concat', 'float', 'int', 'linspace', 'logspace', 'roll', 'select',
'noise', 'randn', 'random_normal', 'rand', 'randu', 'random_uniform', 'randc', 'random_cauchy', 'rande',
'random_exponential', 'randln', 'random_log_normal', 'randb', 'random_bernoulli', 'randp', 'random_poisson', 'randg',
'random_gamma', 'randbeta', 'random_beta', 'randl', 'random_laplace', 'randgumbel', 'random_gumbel', 'randw',
'random_weibull', 'randchi2', 'random_chi2', 'randt', 'random_studentt', 'perlin', 'perlin_noise', 'cellular', 'voronoi',
'worley', 'cellular_noise', 'voronoi_noise', 'plasma', 'turbulence', 'plasma_noise', 'ridged', 'ridged_noise',
'domain_warp', 'domain_warp_noise',
'upper', 'lower', 'split', 'join', 'substring', 'substr', 'find', 'trim', 'replace',
'dilate', 'erode', 'morph_open', 'morph_close',
'rgb_to_hsv', 'hsv_to_rgb',
'rgb_to_oklab', 'oklab_to_rgb', 'rgb_to_cielab', 'cielab_to_rgb',
'int_to_rgb', 'rgb_to_int',
'where', 'histogram', 'hist', 'flow_mag', 'flow_magnitude', 'flow_ang', 'flow_angle',
'interpolate_linear', 'interpolate_area', 'interpolate_nearest', 'interpolate_nearest_exact'
]);
const BRACKET_PAIRS: Record<string, string> = { '(': ')', '[': ']', '{': '}' };
const OPENING_BRACKETS = new Set(['(', '[', '{']);
const CLOSING_BRACKETS = new Set([')', ']', '}']);
@@ -464,6 +434,7 @@ const editorStyle = computed(() => {
// Setup observers
let resizeObserver: ResizeObserver | null = null;
let autocompleteController: { destroy: () => void } | null = null;
onMounted(() => {
refresh();
@@ -477,9 +448,16 @@ onMounted(() => {
});
resizeObserver.observe(editorContainerRef.value);
}
nextTick(() => {
if (textareaRef.value && editorContainerRef.value) {
autocompleteController = attachAutocomplete(textareaRef.value, editorContainerRef.value);
}
});
});
onBeforeUnmount(() => {
autocompleteController?.destroy();
if (resizeObserver) {
resizeObserver.disconnect();
}
+417
View File
@@ -0,0 +1,417 @@
import { FUNCTIONS, KEYWORDS, CONSTANTS, FUNCTION_META } from "./inbuilt_symbols.js";
const AUTOCOMPLETE_STYLE_ID = "mrmth-script-autocomplete-styles";
function ensureAutocompleteStyles() {
if (document.getElementById(AUTOCOMPLETE_STYLE_ID)) return;
const style = document.createElement("style");
style.id = AUTOCOMPLETE_STYLE_ID;
style.textContent = `
.mrmth-autocomplete {
position: absolute;
z-index: 20;
min-width: 180px;
max-width: min(420px, 100%);
max-height: 220px;
overflow-y: auto;
margin: 0;
padding: 4px 0;
list-style: none;
border: 1px solid rgba(255, 255, 255, 0.18);
border-radius: 8px;
background: rgba(18, 20, 24, 0.97);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
font-family: monospace;
font-size: 12px;
}
.mrmth-autocomplete-item {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
padding: 5px 10px;
cursor: pointer;
color: #ecf0f1;
}
.mrmth-autocomplete-item.is-selected {
background: rgba(142, 68, 173, 0.35);
}
.mrmth-autocomplete-item .mrmth-ac-kind {
flex-shrink: 0;
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.04em;
color: #9aa0a6;
}
.mrmth-autocomplete-item .mrmth-ac-name {
color: #c39bd3;
font-weight: 600;
}
.mrmth-autocomplete-item.is-keyword .mrmth-ac-name {
color: #e74c3c;
}
.mrmth-autocomplete-item.is-constant .mrmth-ac-name {
color: #f1c40f;
}
.mrmth-autocomplete-item .mrmth-ac-hint {
color: #7f8c8d;
font-size: 11px;
white-space: nowrap;
}
`;
document.head.appendChild(style);
}
function isCursorInStringOrComment(text, cursor) {
let inString = false;
let stringChar = "";
let inLineComment = false;
let inBlockComment = false;
for (let i = 0; i < cursor; i++) {
const ch = text[i];
const next = text[i + 1];
if (inLineComment) {
if (ch === "\n") inLineComment = false;
continue;
}
if (inBlockComment) {
if (ch === "*" && next === "/") {
inBlockComment = false;
i++;
}
continue;
}
if (inString) {
if (ch === "\\") {
i++;
continue;
}
if (ch === stringChar) inString = false;
continue;
}
if (ch === "#") {
inLineComment = true;
continue;
}
if (ch === "/" && next === "*") {
inBlockComment = true;
i++;
continue;
}
if (ch === '"' || ch === "'") {
inString = true;
stringChar = ch;
}
}
return inString || inBlockComment;
}
function getWordPrefix(text, cursor) {
const before = text.slice(0, cursor);
const match = before.match(/[a-zA-Z_][a-zA-Z0-9_]*$/);
if (!match) return null;
return { word: match[0], start: cursor - match[0].length };
}
function formatArgHint(meta) {
if (!meta) return "";
if (meta.maxArgs === null) {
if (meta.minArgs <= 1) return "(…)";
return `(${meta.minArgs}+ args)`;
}
if (meta.minArgs === meta.maxArgs) {
if (meta.minArgs === 0) return "()";
if (meta.minArgs === 1) return "(x)";
return `(${meta.minArgs} args)`;
}
return `(${meta.minArgs}–${meta.maxArgs} args)`;
}
function buildSuggestions(prefix) {
const lower = prefix.toLowerCase();
const items = [];
for (const name of FUNCTIONS) {
if (!name.toLowerCase().startsWith(lower)) continue;
const meta = FUNCTION_META[name];
items.push({
name,
kind: "function",
hint: meta?.snippet || `${name}()`,
detail: formatArgHint(meta),
insertText: meta?.snippet || `${name}()`,
});
}
for (const name of KEYWORDS) {
if (!name.toLowerCase().startsWith(lower)) continue;
items.push({
name,
kind: "keyword",
hint: name,
detail: "",
insertText: name,
});
}
for (const name of CONSTANTS) {
if (!name.toLowerCase().startsWith(lower)) continue;
items.push({
name,
kind: "constant",
hint: name,
detail: "",
insertText: name,
});
}
items.sort((a, b) => {
const kindOrder = { function: 0, constant: 1, keyword: 2 };
const ka = kindOrder[a.kind] ?? 9;
const kb = kindOrder[b.kind] ?? 9;
if (ka !== kb) return ka - kb;
return a.name.localeCompare(b.name);
});
return items.slice(0, 40);
}
function getCaretCoordinates(textarea, position) {
const style = window.getComputedStyle(textarea);
const div = document.createElement("div");
const props = [
"fontFamily", "fontSize", "fontWeight", "fontStyle", "lineHeight",
"letterSpacing", "textTransform", "wordSpacing", "textIndent",
"whiteSpace", "wordBreak", "overflowWrap", "paddingTop", "paddingRight",
"paddingBottom", "paddingLeft", "borderTopWidth", "borderRightWidth",
"borderBottomWidth", "borderLeftWidth", "boxSizing",
];
div.style.position = "absolute";
div.style.visibility = "hidden";
div.style.whiteSpace = "pre-wrap";
div.style.overflowWrap = style.overflowWrap;
div.style.wordBreak = style.wordBreak;
div.style.width = `${textarea.clientWidth}px`;
for (const prop of props) {
div.style[prop] = style[prop];
}
const text = textarea.value.substring(0, position);
const span = document.createElement("span");
span.textContent = text;
div.appendChild(span);
const marker = document.createElement("span");
marker.textContent = textarea.value.substring(position) || ".";
div.appendChild(marker);
document.body.appendChild(div);
const top = marker.offsetTop;
const left = marker.offsetLeft;
div.remove();
return { top, left };
}
/**
* Attach identifier autocomplete to a script textarea.
* @param {HTMLTextAreaElement} textarea
* @param {HTMLElement} containerEl - positioned parent (e.g. .mrmth-editor-container)
* @returns {{ destroy: () => void, close: () => void }}
*/
export function attachAutocomplete(textarea, containerEl) {
ensureAutocompleteStyles();
const dropdown = document.createElement("ul");
dropdown.className = "mrmth-autocomplete";
dropdown.hidden = true;
containerEl.appendChild(dropdown);
let items = [];
let selectedIndex = 0;
let prefixInfo = null;
let open = false;
const close = () => {
open = false;
dropdown.hidden = true;
dropdown.innerHTML = "";
items = [];
selectedIndex = 0;
prefixInfo = null;
};
const render = () => {
dropdown.innerHTML = "";
items.forEach((item, index) => {
const li = document.createElement("li");
li.className = `mrmth-autocomplete-item is-${item.kind}${index === selectedIndex ? " is-selected" : ""}`;
li.dataset.index = String(index);
const left = document.createElement("span");
left.className = "mrmth-ac-name";
left.textContent = item.name;
const right = document.createElement("span");
right.className = "mrmth-ac-hint";
right.textContent = item.detail || item.hint;
const kind = document.createElement("span");
kind.className = "mrmth-ac-kind";
kind.textContent = item.kind;
li.appendChild(left);
li.appendChild(right);
li.appendChild(kind);
li.addEventListener("mousedown", (e) => {
e.preventDefault();
selectedIndex = index;
applySelected();
});
dropdown.appendChild(li);
});
};
const positionDropdown = () => {
if (!prefixInfo) return;
const coords = getCaretCoordinates(textarea, prefixInfo.start);
dropdown.style.left = `${coords.left}px`;
dropdown.style.top = `${coords.top + 18}px`;
};
const openWith = (nextItems, nextPrefix) => {
if (!nextItems.length) {
close();
return;
}
items = nextItems;
selectedIndex = 0;
prefixInfo = nextPrefix;
open = true;
dropdown.hidden = false;
render();
positionDropdown();
};
const applySelected = () => {
if (!open || !prefixInfo || !items[selectedIndex]) return;
const item = items[selectedIndex];
const before = textarea.value.slice(0, prefixInfo.start);
const after = textarea.value.slice(textarea.selectionEnd);
const insert = item.insertText;
textarea.value = `${before}${insert}${after}`;
const cursorPos = before.length + insert.length;
if (item.kind === "function" && insert.endsWith("()")) {
textarea.selectionStart = textarea.selectionEnd = cursorPos - 1;
} else {
textarea.selectionStart = textarea.selectionEnd = cursorPos;
}
textarea.dispatchEvent(new Event("input", { bubbles: true }));
close();
};
const maybeOpen = (force = false) => {
const cursor = textarea.selectionStart;
if (cursor !== textarea.selectionEnd) {
close();
return;
}
if (isCursorInStringOrComment(textarea.value, cursor)) {
close();
return;
}
const info = getWordPrefix(textarea.value, cursor);
if (!info || (!force && info.word.length < 1)) {
close();
return;
}
const suggestions = buildSuggestions(info.word);
if (!suggestions.length) {
close();
return;
}
openWith(suggestions, info);
};
const onInput = () => {
if (!open) {
const info = getWordPrefix(textarea.value, textarea.selectionStart);
if (info && info.word.length >= 2) {
maybeOpen(false);
}
return;
}
maybeOpen(false);
};
const onKeyDown = (e) => {
if (e.ctrlKey && e.key === " ") {
e.preventDefault();
maybeOpen(true);
return;
}
if (!open) return;
if (e.key === "ArrowDown") {
e.preventDefault();
selectedIndex = (selectedIndex + 1) % items.length;
render();
return;
}
if (e.key === "ArrowUp") {
e.preventDefault();
selectedIndex = (selectedIndex - 1 + items.length) % items.length;
render();
return;
}
if (e.key === "Enter" || e.key === "Tab") {
e.preventDefault();
applySelected();
return;
}
if (e.key === "Escape") {
e.preventDefault();
close();
}
};
const onBlur = () => {
setTimeout(close, 120);
};
const onScroll = () => {
if (open) positionDropdown();
};
textarea.addEventListener("input", onInput);
textarea.addEventListener("keydown", onKeyDown);
textarea.addEventListener("blur", onBlur);
textarea.addEventListener("scroll", onScroll);
return {
close,
destroy() {
close();
dropdown.remove();
textarea.removeEventListener("input", onInput);
textarea.removeEventListener("keydown", onKeyDown);
textarea.removeEventListener("blur", onBlur);
textarea.removeEventListener("scroll", onScroll);
},
};
}
export { buildSuggestions, getWordPrefix, isCursorInStringOrComment, formatArgHint };
+238 -1
View File
@@ -10,4 +10,241 @@ export const CONSTANTS = new Set([
export const FUNCTIONS = new Set([
"abs", "acos", "acosh", "all", "angle", "any", "append", "argmax", "argmin", "argsort", "asin", "asinh", "atan", "atan2", "atanh", "band", "batch_shuffle", "bitcount", "bitwise_and", "bitwise_not", "bitwise_or", "bitwise_xor", "blur", "bnot", "bor", "botk", "botk_ind", "botk_indices", "bxor", "cat", "ceil", "cellular", "cellular_noise", "cielab_to_rgb", "clamp", "cnt", "concat", "concatenate", "conv", "convolution", "corr", "correlation", "cos", "cosh", "cosine_similarity", "cossim", "count", "cov", "crop", "cross", "cubic", "cubic_ease", "cumprod", "cumsum", "dilate", "dist", "distance", "domain_warp", "domain_warp_noise", "dot", "edge", "elastic", "elastic_ease", "entropy", "erf", "erfinv", "erode", "exp", "ezconv", "ezconvolution", "fft", "find", "flatten", "flip", "float", "floor", "flow_ang", "flow_angle", "flow_apply", "flow_mag", "flow_magnitude", "flow_to_image", "fract", "gamma", "gaussian", "gelu", "get_value", "hist", "histogram", "hsv_to_rgb", "ifft", "int", "int_to_rgb", "interpolate_area", "interpolate_linear", "interpolate_nearest", "interpolate_nearest_exact", "join", "length", "lerp", "linspace", "ln", "log", "logspace", "lower", "map", "matmul", "mean", "median", "mode", "moment", "morph_close", "morph_open", "motion_mask", "nan_to_num", "noise", "now", "nvl", "oklab_to_rgb", "overlay", "pad", "percentile", "perlin", "perlin_noise", "perm", "permute", "pinv", "plasma", "plasma_noise", "popcnt", "popcount", "pow", "prcnt", "print", "print_shape", "pshp", "quantile", "quartil", "quartile", "rand", "randb", "randbeta", "randc", "rande", "randg", "randgumbel", "randchi2", "randl", "randln", "randn", "random_bernoulli", "random_beta", "random_cauchy", "random_exponential", "random_gamma", "random_gumbel", "random_chi2", "random_laplace", "random_log_normal", "random_normal", "random_poisson", "random_studentt", "random_uniform", "random_weibull", "randp", "randt", "randu", "randw", "range", "relu", "remap", "replace", "reshape", "rgb_to_cielab", "rgb_to_hsv", "rgb_to_int", "rgb_to_oklab", "ridged", "ridged_noise", "rife", "roll", "round", "rshp", "select", "shape", "shuffle", "sigm", "sign", "sin", "sine", "sine_ease", "sinh", "smax", "smin", "smootherstep", "smoothstep", "snorm", "softmax", "softmin", "softplus", "sort", "split", "sqrt", "stack_clear", "stack_get", "stack_has", "stack_pop", "stack_push", "std", "step", "substr", "substring", "sum", "swap", "tan", "tanh", "tensor", "text_image", "timestamp", "tmax", "tmin", "tnorm", "topk", "topk_ind", "topk_indices", "trim", "turbulence", "unique", "upper", "var", "voronoi", "voronoi_noise", "where", "worley"
]);
]);
export const FUNCTION_META = {
abs: { minArgs: 1, maxArgs: 1, snippet: "abs()" },
acos: { minArgs: 1, maxArgs: 1, snippet: "acos()" },
acosh: { minArgs: 1, maxArgs: 1, snippet: "acosh()" },
all: { minArgs: 1, maxArgs: 1, snippet: "all()" },
angle: { minArgs: 1, maxArgs: 1, snippet: "angle()" },
any: { minArgs: 1, maxArgs: 1, snippet: "any()" },
append: { minArgs: 2, maxArgs: 2, snippet: "append()" },
argmax: { minArgs: 1, maxArgs: 1, snippet: "argmax()" },
argmin: { minArgs: 1, maxArgs: 1, snippet: "argmin()" },
argsort: { minArgs: 1, maxArgs: 2, snippet: "argsort()" },
asin: { minArgs: 1, maxArgs: 1, snippet: "asin()" },
asinh: { minArgs: 1, maxArgs: 1, snippet: "asinh()" },
atan: { minArgs: 1, maxArgs: 1, snippet: "atan()" },
atan2: { minArgs: 2, maxArgs: 2, snippet: "atan2()" },
atanh: { minArgs: 1, maxArgs: 1, snippet: "atanh()" },
band: { minArgs: 2, maxArgs: 2, snippet: "band()" },
batch_shuffle: { minArgs: 2, maxArgs: 2, snippet: "batch_shuffle()" },
bitcount: { minArgs: 1, maxArgs: 1, snippet: "bitcount()" },
bitwise_and: { minArgs: 2, maxArgs: 2, snippet: "bitwise_and()" },
bitwise_not: { minArgs: 1, maxArgs: 1, snippet: "bitwise_not()" },
bitwise_or: { minArgs: 2, maxArgs: 2, snippet: "bitwise_or()" },
bitwise_xor: { minArgs: 2, maxArgs: 2, snippet: "bitwise_xor()" },
blur: { minArgs: 2, maxArgs: 3, snippet: "blur()" },
bnot: { minArgs: 1, maxArgs: 1, snippet: "bnot()" },
bor: { minArgs: 2, maxArgs: 2, snippet: "bor()" },
botk: { minArgs: 2, maxArgs: 2, snippet: "botk()" },
botk_ind: { minArgs: 2, maxArgs: 2, snippet: "botk_ind()" },
botk_indices: { minArgs: 2, maxArgs: 2, snippet: "botk_indices()" },
bxor: { minArgs: 2, maxArgs: 2, snippet: "bxor()" },
cat: { minArgs: 2, maxArgs: null, snippet: "cat()" },
ceil: { minArgs: 1, maxArgs: 1, snippet: "ceil()" },
cellular: { minArgs: 2, maxArgs: 5, snippet: "cellular()" },
cellular_noise: { minArgs: 2, maxArgs: 5, snippet: "cellular_noise()" },
cielab_to_rgb: { minArgs: 1, maxArgs: 3, snippet: "cielab_to_rgb()" },
clamp: { minArgs: 3, maxArgs: 3, snippet: "clamp()" },
cnt: { minArgs: 1, maxArgs: 1, snippet: "cnt()" },
concat: { minArgs: 2, maxArgs: null, snippet: "concat()" },
concatenate: { minArgs: 2, maxArgs: null, snippet: "concatenate()" },
conv: { minArgs: 2, maxArgs: null, snippet: "conv()" },
convolution: { minArgs: 2, maxArgs: null, snippet: "convolution()" },
corr: { minArgs: 2, maxArgs: 2, snippet: "corr()" },
correlation: { minArgs: 2, maxArgs: 2, snippet: "correlation()" },
cos: { minArgs: 1, maxArgs: 1, snippet: "cos()" },
cosh: { minArgs: 1, maxArgs: 1, snippet: "cosh()" },
cosine_similarity: { minArgs: 2, maxArgs: 2, snippet: "cosine_similarity()" },
cossim: { minArgs: 2, maxArgs: 2, snippet: "cossim()" },
count: { minArgs: 1, maxArgs: 1, snippet: "count()" },
cov: { minArgs: 2, maxArgs: 2, snippet: "cov()" },
crop: { minArgs: 3, maxArgs: 3, snippet: "crop()" },
cross: { minArgs: 2, maxArgs: 2, snippet: "cross()" },
cubic: { minArgs: 3, maxArgs: 3, snippet: "cubic()" },
cubic_ease: { minArgs: 3, maxArgs: 3, snippet: "cubic_ease()" },
cumprod: { minArgs: 1, maxArgs: 1, snippet: "cumprod()" },
cumsum: { minArgs: 1, maxArgs: 1, snippet: "cumsum()" },
dilate: { minArgs: 1, maxArgs: 2, snippet: "dilate()" },
dist: { minArgs: 4, maxArgs: 4, snippet: "dist()" },
distance: { minArgs: 4, maxArgs: 4, snippet: "distance()" },
domain_warp: { minArgs: 4, maxArgs: 8, snippet: "domain_warp()" },
domain_warp_noise: { minArgs: 4, maxArgs: 8, snippet: "domain_warp_noise()" },
dot: { minArgs: 2, maxArgs: 2, snippet: "dot()" },
edge: { minArgs: 1, maxArgs: 2, snippet: "edge()" },
elastic: { minArgs: 3, maxArgs: 3, snippet: "elastic()" },
elastic_ease: { minArgs: 3, maxArgs: 3, snippet: "elastic_ease()" },
entropy: { minArgs: 1, maxArgs: 1, snippet: "entropy()" },
erf: { minArgs: 1, maxArgs: 1, snippet: "erf()" },
erfinv: { minArgs: 1, maxArgs: 1, snippet: "erfinv()" },
erode: { minArgs: 1, maxArgs: 2, snippet: "erode()" },
exp: { minArgs: 1, maxArgs: 1, snippet: "exp()" },
ezconv: { minArgs: 2, maxArgs: null, snippet: "ezconv()" },
ezconvolution: { minArgs: 2, maxArgs: null, snippet: "ezconvolution()" },
fft: { minArgs: 1, maxArgs: 1, snippet: "fft()" },
find: { minArgs: 2, maxArgs: 2, snippet: "find()" },
flatten: { minArgs: 1, maxArgs: 1, snippet: "flatten()" },
flip: { minArgs: 2, maxArgs: 2, snippet: "flip()" },
float: { minArgs: 1, maxArgs: 1, snippet: "float()" },
floor: { minArgs: 1, maxArgs: 1, snippet: "floor()" },
flow_ang: { minArgs: 1, maxArgs: 1, snippet: "flow_ang()" },
flow_angle: { minArgs: 1, maxArgs: 1, snippet: "flow_angle()" },
flow_apply: { minArgs: 2, maxArgs: 2, snippet: "flow_apply()" },
flow_mag: { minArgs: 1, maxArgs: 1, snippet: "flow_mag()" },
flow_magnitude: { minArgs: 1, maxArgs: 1, snippet: "flow_magnitude()" },
flow_to_image: { minArgs: 1, maxArgs: 1, snippet: "flow_to_image()" },
fract: { minArgs: 1, maxArgs: 1, snippet: "fract()" },
gamma: { minArgs: 1, maxArgs: 1, snippet: "gamma()" },
gaussian: { minArgs: 2, maxArgs: 3, snippet: "gaussian()" },
gelu: { minArgs: 1, maxArgs: 1, snippet: "gelu()" },
get_value: { minArgs: 2, maxArgs: 2, snippet: "get_value()" },
hist: { minArgs: 4, maxArgs: 4, snippet: "hist()" },
histogram: { minArgs: 4, maxArgs: 4, snippet: "histogram()" },
hsv_to_rgb: { minArgs: 1, maxArgs: 4, snippet: "hsv_to_rgb()" },
ifft: { minArgs: 1, maxArgs: 2, snippet: "ifft()" },
int: { minArgs: 1, maxArgs: 1, snippet: "int()" },
int_to_rgb: { minArgs: 1, maxArgs: 1, snippet: "int_to_rgb()" },
interpolate_area: { minArgs: 2, maxArgs: 2, snippet: "interpolate_area()" },
interpolate_linear: { minArgs: 2, maxArgs: 2, snippet: "interpolate_linear()" },
interpolate_nearest: { minArgs: 2, maxArgs: 2, snippet: "interpolate_nearest()" },
interpolate_nearest_exact: { minArgs: 2, maxArgs: 2, snippet: "interpolate_nearest_exact()" },
join: { minArgs: 1, maxArgs: 2, snippet: "join()" },
length: { minArgs: 1, maxArgs: 1, snippet: "length()" },
lerp: { minArgs: 3, maxArgs: 3, snippet: "lerp()" },
linspace: { minArgs: 3, maxArgs: 3, snippet: "linspace()" },
ln: { minArgs: 1, maxArgs: 1, snippet: "ln()" },
log: { minArgs: 1, maxArgs: 1, snippet: "log()" },
logspace: { minArgs: 4, maxArgs: 4, snippet: "logspace()" },
lower: { minArgs: 1, maxArgs: 1, snippet: "lower()" },
map: { minArgs: 2, maxArgs: null, snippet: "map()" },
matmul: { minArgs: 2, maxArgs: 2, snippet: "matmul()" },
mean: { minArgs: 1, maxArgs: 1, snippet: "mean()" },
median: { minArgs: 1, maxArgs: 1, snippet: "median()" },
mode: { minArgs: 1, maxArgs: 1, snippet: "mode()" },
moment: { minArgs: 3, maxArgs: 3, snippet: "moment()" },
morph_close: { minArgs: 1, maxArgs: 2, snippet: "morph_close()" },
morph_open: { minArgs: 1, maxArgs: 2, snippet: "morph_open()" },
motion_mask: { minArgs: 1, maxArgs: 1, snippet: "motion_mask()" },
nan_to_num: { minArgs: 4, maxArgs: 4, snippet: "nan_to_num()" },
noise: { minArgs: 1, maxArgs: 2, snippet: "noise()" },
now: { minArgs: 0, maxArgs: 0, snippet: "now()" },
nvl: { minArgs: 4, maxArgs: 4, snippet: "nvl()" },
oklab_to_rgb: { minArgs: 1, maxArgs: 3, snippet: "oklab_to_rgb()" },
overlay: { minArgs: 3, maxArgs: 4, snippet: "overlay()" },
pad: { minArgs: 2, maxArgs: 2, snippet: "pad()" },
percentile: { minArgs: 2, maxArgs: 2, snippet: "percentile()" },
perlin: { minArgs: 2, maxArgs: 5, snippet: "perlin()" },
perlin_noise: { minArgs: 2, maxArgs: 5, snippet: "perlin_noise()" },
perm: { minArgs: 2, maxArgs: 2, snippet: "perm()" },
permute: { minArgs: 2, maxArgs: 2, snippet: "permute()" },
pinv: { minArgs: 1, maxArgs: 1, snippet: "pinv()" },
plasma: { minArgs: 2, maxArgs: 5, snippet: "plasma()" },
plasma_noise: { minArgs: 2, maxArgs: 5, snippet: "plasma_noise()" },
popcnt: { minArgs: 1, maxArgs: 1, snippet: "popcnt()" },
popcount: { minArgs: 1, maxArgs: 1, snippet: "popcount()" },
pow: { minArgs: 2, maxArgs: 2, snippet: "pow()" },
prcnt: { minArgs: 2, maxArgs: 2, snippet: "prcnt()" },
print: { minArgs: 1, maxArgs: 1, snippet: "print()" },
print_shape: { minArgs: 1, maxArgs: 1, snippet: "print_shape()" },
pshp: { minArgs: 1, maxArgs: 1, snippet: "pshp()" },
quantile: { minArgs: 2, maxArgs: 2, snippet: "quantile()" },
quartil: { minArgs: 2, maxArgs: 2, snippet: "quartil()" },
quartile: { minArgs: 2, maxArgs: 2, snippet: "quartile()" },
rand: { minArgs: 1, maxArgs: 2, snippet: "rand()" },
randb: { minArgs: 2, maxArgs: 3, snippet: "randb()" },
randbeta: { minArgs: 3, maxArgs: 4, snippet: "randbeta()" },
randc: { minArgs: 3, maxArgs: 4, snippet: "randc()" },
rande: { minArgs: 2, maxArgs: 3, snippet: "rande()" },
randg: { minArgs: 3, maxArgs: 4, snippet: "randg()" },
randgumbel: { minArgs: 3, maxArgs: 4, snippet: "randgumbel()" },
randchi2: { minArgs: 2, maxArgs: 3, snippet: "randchi2()" },
randl: { minArgs: 3, maxArgs: 4, snippet: "randl()" },
randln: { minArgs: 3, maxArgs: 4, snippet: "randln()" },
randn: { minArgs: 1, maxArgs: 2, snippet: "randn()" },
random_bernoulli: { minArgs: 2, maxArgs: 3, snippet: "random_bernoulli()" },
random_beta: { minArgs: 3, maxArgs: 4, snippet: "random_beta()" },
random_cauchy: { minArgs: 3, maxArgs: 4, snippet: "random_cauchy()" },
random_exponential: { minArgs: 2, maxArgs: 3, snippet: "random_exponential()" },
random_gamma: { minArgs: 3, maxArgs: 4, snippet: "random_gamma()" },
random_gumbel: { minArgs: 3, maxArgs: 4, snippet: "random_gumbel()" },
random_chi2: { minArgs: 2, maxArgs: 3, snippet: "random_chi2()" },
random_laplace: { minArgs: 3, maxArgs: 4, snippet: "random_laplace()" },
random_log_normal: { minArgs: 3, maxArgs: 4, snippet: "random_log_normal()" },
random_normal: { minArgs: 1, maxArgs: 2, snippet: "random_normal()" },
random_poisson: { minArgs: 2, maxArgs: 3, snippet: "random_poisson()" },
random_studentt: { minArgs: 2, maxArgs: 3, snippet: "random_studentt()" },
random_uniform: { minArgs: 1, maxArgs: 2, snippet: "random_uniform()" },
random_weibull: { minArgs: 3, maxArgs: 4, snippet: "random_weibull()" },
randp: { minArgs: 2, maxArgs: 3, snippet: "randp()" },
randt: { minArgs: 2, maxArgs: 3, snippet: "randt()" },
randu: { minArgs: 1, maxArgs: 2, snippet: "randu()" },
randw: { minArgs: 3, maxArgs: 4, snippet: "randw()" },
range: { minArgs: 3, maxArgs: 3, snippet: "range()" },
relu: { minArgs: 1, maxArgs: null, snippet: "relu()" },
remap: { minArgs: 5, maxArgs: 5, snippet: "remap()" },
replace: { minArgs: 3, maxArgs: 3, snippet: "replace()" },
reshape: { minArgs: 2, maxArgs: 2, snippet: "reshape()" },
rgb_to_cielab: { minArgs: 1, maxArgs: 3, snippet: "rgb_to_cielab()" },
rgb_to_hsv: { minArgs: 1, maxArgs: 4, snippet: "rgb_to_hsv()" },
rgb_to_int: { minArgs: 1, maxArgs: 3, snippet: "rgb_to_int()" },
rgb_to_oklab: { minArgs: 1, maxArgs: 3, snippet: "rgb_to_oklab()" },
ridged: { minArgs: 2, maxArgs: 5, snippet: "ridged()" },
ridged_noise: { minArgs: 2, maxArgs: 5, snippet: "ridged_noise()" },
rife: { minArgs: 4, maxArgs: 5, snippet: "rife()" },
roll: { minArgs: 2, maxArgs: 3, snippet: "roll()" },
round: { minArgs: 1, maxArgs: 1, snippet: "round()" },
rshp: { minArgs: 2, maxArgs: 2, snippet: "rshp()" },
select: { minArgs: 2, maxArgs: 2, snippet: "select()" },
shape: { minArgs: 1, maxArgs: 1, snippet: "shape()" },
shuffle: { minArgs: 2, maxArgs: 2, snippet: "shuffle()" },
sigm: { minArgs: 1, maxArgs: 1, snippet: "sigm()" },
sign: { minArgs: 1, maxArgs: 1, snippet: "sign()" },
sin: { minArgs: 1, maxArgs: 1, snippet: "sin()" },
sine: { minArgs: 3, maxArgs: 3, snippet: "sine()" },
sine_ease: { minArgs: 3, maxArgs: 3, snippet: "sine_ease()" },
sinh: { minArgs: 1, maxArgs: 1, snippet: "sinh()" },
smax: { minArgs: 1, maxArgs: null, snippet: "smax()" },
smin: { minArgs: 1, maxArgs: null, snippet: "smin()" },
smootherstep: { minArgs: 3, maxArgs: 3, snippet: "smootherstep()" },
smoothstep: { minArgs: 3, maxArgs: 3, snippet: "smoothstep()" },
snorm: { minArgs: 1, maxArgs: 1, snippet: "snorm()" },
softmax: { minArgs: 1, maxArgs: 2, snippet: "softmax()" },
softmin: { minArgs: 1, maxArgs: 2, snippet: "softmin()" },
softplus: { minArgs: 1, maxArgs: 1, snippet: "softplus()" },
sort: { minArgs: 1, maxArgs: 1, snippet: "sort()" },
split: { minArgs: 1, maxArgs: 2, snippet: "split()" },
sqrt: { minArgs: 1, maxArgs: 1, snippet: "sqrt()" },
stack_clear: { minArgs: 1, maxArgs: 1, snippet: "stack_clear()" },
stack_get: { minArgs: 1, maxArgs: 1, snippet: "stack_get()" },
stack_has: { minArgs: 1, maxArgs: 1, snippet: "stack_has()" },
stack_pop: { minArgs: 1, maxArgs: 1, snippet: "stack_pop()" },
stack_push: { minArgs: 2, maxArgs: 2, snippet: "stack_push()" },
std: { minArgs: 1, maxArgs: 1, snippet: "std()" },
step: { minArgs: 2, maxArgs: 2, snippet: "step()" },
substr: { minArgs: 2, maxArgs: 3, snippet: "substr()" },
substring: { minArgs: 2, maxArgs: 3, snippet: "substring()" },
sum: { minArgs: 1, maxArgs: 1, snippet: "sum()" },
swap: { minArgs: 4, maxArgs: 4, snippet: "swap()" },
tan: { minArgs: 1, maxArgs: 1, snippet: "tan()" },
tanh: { minArgs: 1, maxArgs: 1, snippet: "tanh()" },
tensor: { minArgs: 2, maxArgs: 3, snippet: "tensor()" },
text_image: { minArgs: 3, maxArgs: 9, snippet: "text_image()" },
timestamp: { minArgs: 0, maxArgs: 0, snippet: "timestamp()" },
tmax: { minArgs: 2, maxArgs: 2, snippet: "tmax()" },
tmin: { minArgs: 2, maxArgs: 2, snippet: "tmin()" },
tnorm: { minArgs: 1, maxArgs: 1, snippet: "tnorm()" },
topk: { minArgs: 2, maxArgs: 2, snippet: "topk()" },
topk_ind: { minArgs: 2, maxArgs: 2, snippet: "topk_ind()" },
topk_indices: { minArgs: 2, maxArgs: 2, snippet: "topk_indices()" },
trim: { minArgs: 1, maxArgs: 1, snippet: "trim()" },
turbulence: { minArgs: 2, maxArgs: 5, snippet: "turbulence()" },
unique: { minArgs: 1, maxArgs: 1, snippet: "unique()" },
upper: { minArgs: 1, maxArgs: 1, snippet: "upper()" },
var: { minArgs: 1, maxArgs: 1, snippet: "var()" },
voronoi: { minArgs: 2, maxArgs: 5, snippet: "voronoi()" },
voronoi_noise: { minArgs: 2, maxArgs: 5, snippet: "voronoi_noise()" },
where: { minArgs: 3, maxArgs: 3, snippet: "where()" },
worley: { minArgs: 2, maxArgs: 5, snippet: "worley()" },
};
+3
View File
@@ -1,5 +1,6 @@
import { app } from "/scripts/app.js";
import { KEYWORDS, CONSTANTS, FUNCTIONS } from "./inbuilt_symbols.js";
import { attachAutocomplete } from "./autocomplete.js";
const STYLE_ID = "mrmth-script-line-numbers-unified";
@@ -167,6 +168,7 @@ if (!window.__mrmthScriptInputUnifiedInit) {
function isLikelyScriptTextarea(el) {
if (!(el instanceof HTMLTextAreaElement)) return false;
if (el.dataset.mrmthEnhanced === "1") return false;
if (el.closest('[data-mrmth-script-widget="vue"]')) return false;
const attrs = [
el.name,
@@ -473,6 +475,7 @@ if (!window.__mrmthScriptInputUnifiedInit) {
textarea.autocorrect = "off";
addBracketAutoClose(textarea);
attachAutocomplete(textarea, editorContainer);
const cs = getComputedStyle(textarea);
syntaxLayer.style.fontFamily = cs.fontFamily;
syntaxLayer.style.fontSize = cs.fontSize;