kumi 0.0.26 → 0.0.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/CLAUDE.md +4 -0
- data/README.md +17 -8
- data/data/functions/core/conversion.yaml +32 -0
- data/data/kernels/javascript/core/coercion.yaml +20 -0
- data/data/kernels/ruby/core/coercion.yaml +20 -0
- data/docs/ARCHITECTURE.md +277 -0
- data/docs/DEVELOPMENT.md +62 -0
- data/docs/FUNCTIONS.md +955 -0
- data/docs/SYNTAX.md +8 -0
- data/docs/VSCODE_EXTENSION.md +114 -0
- data/docs/functions-reference.json +1821 -0
- data/golden/array_element/expected/schema_ruby.rb +1 -1
- data/golden/array_index/expected/schema_ruby.rb +1 -1
- data/golden/array_operations/expected/schema_ruby.rb +1 -1
- data/golden/cascade_logic/expected/schema_ruby.rb +1 -1
- data/golden/chained_fusion/expected/schema_ruby.rb +1 -1
- data/golden/decimal_explicit/expected/ast.txt +38 -0
- data/golden/decimal_explicit/expected/input_plan.txt +3 -0
- data/golden/decimal_explicit/expected/lir_00_unoptimized.txt +30 -0
- data/golden/decimal_explicit/expected/lir_01_hoist_scalar_references.txt +30 -0
- data/golden/decimal_explicit/expected/lir_02_inlined.txt +44 -0
- data/golden/decimal_explicit/expected/lir_03_cse.txt +40 -0
- data/golden/decimal_explicit/expected/lir_04_1_loop_fusion.txt +40 -0
- data/golden/decimal_explicit/expected/lir_04_loop_invcm.txt +40 -0
- data/golden/decimal_explicit/expected/lir_06_const_prop.txt +40 -0
- data/golden/decimal_explicit/expected/nast.txt +30 -0
- data/golden/decimal_explicit/expected/schema_javascript.mjs +31 -0
- data/golden/decimal_explicit/expected/schema_ruby.rb +57 -0
- data/golden/decimal_explicit/expected/snast.txt +30 -0
- data/golden/decimal_explicit/expected.json +1 -0
- data/golden/decimal_explicit/input.json +5 -0
- data/golden/decimal_explicit/schema.kumi +14 -0
- data/golden/element_arrays/expected/schema_ruby.rb +1 -1
- data/golden/empty_and_null_inputs/expected/schema_ruby.rb +1 -1
- data/golden/function_overload/expected/schema_ruby.rb +1 -1
- data/golden/game_of_life/expected/schema_ruby.rb +1 -1
- data/golden/hash_keys/expected/schema_ruby.rb +1 -1
- data/golden/hash_value/expected/schema_ruby.rb +1 -1
- data/golden/hierarchical_complex/expected/schema_ruby.rb +1 -1
- data/golden/inline_rename_scope_leak/expected/schema_ruby.rb +1 -1
- data/golden/input_reference/expected/schema_ruby.rb +1 -1
- data/golden/interleaved_fusion/expected/schema_ruby.rb +1 -1
- data/golden/let_inline/expected/schema_ruby.rb +1 -1
- data/golden/loop_fusion/expected/schema_ruby.rb +1 -1
- data/golden/min_reduce_scope/expected/schema_ruby.rb +1 -1
- data/golden/mixed_dimensions/expected/schema_ruby.rb +1 -1
- data/golden/multirank_hoisting/expected/schema_ruby.rb +1 -1
- data/golden/nested_hash/expected/schema_ruby.rb +1 -1
- data/golden/reduction_broadcast/expected/schema_ruby.rb +1 -1
- data/golden/roll/expected/schema_ruby.rb +1 -1
- data/golden/shift/expected/schema_ruby.rb +1 -1
- data/golden/shift_2d/expected/schema_ruby.rb +1 -1
- data/golden/simple_math/expected/schema_ruby.rb +1 -1
- data/golden/streaming_basics/expected/schema_ruby.rb +1 -1
- data/golden/tuples/expected/schema_ruby.rb +1 -1
- data/golden/tuples_and_arrays/expected/schema_ruby.rb +1 -1
- data/golden/us_tax_2024/expected/schema_ruby.rb +1 -1
- data/golden/with_constants/expected/schema_ruby.rb +1 -1
- data/lib/kumi/configuration.rb +6 -0
- data/lib/kumi/core/input/type_matcher.rb +8 -1
- data/lib/kumi/core/ruby_parser/input_builder.rb +2 -2
- data/lib/kumi/core/types/normalizer.rb +1 -0
- data/lib/kumi/core/types/validator.rb +2 -2
- data/lib/kumi/core/types.rb +2 -2
- data/lib/kumi/dev/golden/reporter.rb +9 -0
- data/lib/kumi/dev/golden/result.rb +3 -1
- data/lib/kumi/dev/golden/runtime_test.rb +25 -0
- data/lib/kumi/dev/golden/suite.rb +4 -4
- data/lib/kumi/dev/golden/value_normalizer.rb +80 -0
- data/lib/kumi/dev/golden.rb +21 -12
- data/lib/kumi/doc_generator/formatters/json.rb +39 -0
- data/lib/kumi/doc_generator/formatters/markdown.rb +175 -0
- data/lib/kumi/doc_generator/loader.rb +37 -0
- data/lib/kumi/doc_generator/merger.rb +54 -0
- data/lib/kumi/doc_generator.rb +4 -0
- data/lib/kumi/version.rb +1 -1
- data/vscode-extension/.gitignore +4 -0
- data/vscode-extension/README.md +59 -0
- data/vscode-extension/TESTING.md +151 -0
- data/vscode-extension/package.json +51 -0
- data/vscode-extension/src/extension.ts +295 -0
- data/vscode-extension/tsconfig.json +15 -0
- metadata +37 -1
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Kumi
|
4
|
+
module DocGenerator
|
5
|
+
class Loader
|
6
|
+
def initialize(functions_dir: nil, kernels_dir: nil)
|
7
|
+
@functions_dir = functions_dir
|
8
|
+
@kernels_dir = kernels_dir
|
9
|
+
end
|
10
|
+
|
11
|
+
def load_functions
|
12
|
+
return [] unless @functions_dir
|
13
|
+
load_yaml_dir(@functions_dir)
|
14
|
+
end
|
15
|
+
|
16
|
+
def load_kernels
|
17
|
+
return [] unless @kernels_dir
|
18
|
+
load_yaml_dir(@kernels_dir)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def load_yaml_dir(dir_path)
|
24
|
+
result = []
|
25
|
+
Dir.glob(File.join(dir_path, '**/*.yaml')).each do |file|
|
26
|
+
data = YAML.load_file(file)
|
27
|
+
if data && data['functions']
|
28
|
+
result.concat(data['functions'])
|
29
|
+
elsif data && data['kernels']
|
30
|
+
result.concat(data['kernels'])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
result
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Kumi
|
2
|
+
module DocGenerator
|
3
|
+
class Merger
|
4
|
+
def initialize(loader)
|
5
|
+
@loader = loader
|
6
|
+
end
|
7
|
+
|
8
|
+
def merge
|
9
|
+
functions = @loader.load_functions
|
10
|
+
kernels = @loader.load_kernels
|
11
|
+
|
12
|
+
result = {}
|
13
|
+
|
14
|
+
functions.each do |fn|
|
15
|
+
aliases = fn['aliases'] || []
|
16
|
+
aliases.each do |alias_name|
|
17
|
+
result[alias_name] = build_doc_entry(fn, kernels)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
result
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def build_doc_entry(function, kernels)
|
27
|
+
kernel_map = {}
|
28
|
+
kernels.each do |kernel|
|
29
|
+
if kernel['fn'] == function['id']
|
30
|
+
target = extract_target(kernel['id'])
|
31
|
+
kernel_map[target] = kernel
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
{
|
36
|
+
'id' => function['id'],
|
37
|
+
'kind' => function['kind'],
|
38
|
+
'params' => function['params'] || [],
|
39
|
+
'arity' => (function['params'] || []).length,
|
40
|
+
'kernels' => kernel_map,
|
41
|
+
'dtype' => function['dtype'],
|
42
|
+
'aliases' => function['aliases'] || [],
|
43
|
+
'reduction_strategy' => function['reduction_strategy']
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
def extract_target(kernel_id)
|
48
|
+
# kernel_id format: "agg.sum:ruby:v1" -> "ruby"
|
49
|
+
parts = kernel_id.split(':')
|
50
|
+
parts[1] if parts.length >= 2
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/kumi/version.rb
CHANGED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Kumi Language Support for VSCode
|
2
|
+
|
3
|
+
VSCode extension providing autocomplete, hover information, and documentation for Kumi functions.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- **Autocomplete** - Function suggestions when typing `fn(:` in Ruby files
|
8
|
+
- **Hover Information** - Type signatures, arity, and parameter info on hover
|
9
|
+
- **Function Reference** - Auto-generated from `docs/functions-reference.json`
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
1. Build the extension:
|
14
|
+
```bash
|
15
|
+
npm install
|
16
|
+
npm run compile
|
17
|
+
```
|
18
|
+
|
19
|
+
2. Install in VSCode:
|
20
|
+
- Open VSCode Command Palette: `Cmd+Shift+P` (Mac) or `Ctrl+Shift+P` (Linux/Windows)
|
21
|
+
- Type "Extensions: Install from VSIX"
|
22
|
+
- Select the built `.vsix` file
|
23
|
+
|
24
|
+
Or load as development extension:
|
25
|
+
- Open VSCode with this folder
|
26
|
+
- Press `F5` to start debugging
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
While editing a Ruby file with Kumi schemas:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
schema do
|
34
|
+
input { float :x }
|
35
|
+
|
36
|
+
# Type `fn(:` and get autocomplete suggestions
|
37
|
+
let :doubled, fn(:mul, input.x, 2)
|
38
|
+
|
39
|
+
# Hover over `mul` to see type info
|
40
|
+
value :result, doubled
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
## Data Source
|
45
|
+
|
46
|
+
Function definitions are loaded from `../../docs/functions-reference.json`, which is auto-generated by:
|
47
|
+
|
48
|
+
```bash
|
49
|
+
bin/kumi-doc-gen
|
50
|
+
```
|
51
|
+
|
52
|
+
Always regenerate the JSON after modifying function definitions!
|
53
|
+
|
54
|
+
## Development
|
55
|
+
|
56
|
+
```bash
|
57
|
+
npm run watch # Watch for TypeScript changes
|
58
|
+
npm run compile # Build once
|
59
|
+
```
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# Testing the Kumi VSCode Extension
|
2
|
+
|
3
|
+
## Quick Start
|
4
|
+
|
5
|
+
### 1. Build the Extension
|
6
|
+
|
7
|
+
```bash
|
8
|
+
cd vscode-extension
|
9
|
+
npm install
|
10
|
+
npm run compile
|
11
|
+
```
|
12
|
+
|
13
|
+
### 2. Generate Function Data
|
14
|
+
|
15
|
+
Before testing, generate the function reference JSON:
|
16
|
+
|
17
|
+
```bash
|
18
|
+
# From kumi root
|
19
|
+
bin/kumi-doc-gen
|
20
|
+
```
|
21
|
+
|
22
|
+
This creates `docs/functions-reference.json` that the extension reads.
|
23
|
+
|
24
|
+
### 3. Launch Extension in Debug Mode
|
25
|
+
|
26
|
+
```bash
|
27
|
+
# From vscode-extension directory
|
28
|
+
code ..
|
29
|
+
```
|
30
|
+
|
31
|
+
Or just open the kumi repo root in VSCode, then:
|
32
|
+
- Press `F5` to start debugging
|
33
|
+
- A new VSCode window will open with the extension loaded
|
34
|
+
|
35
|
+
### 4. Test Autocomplete and Hover
|
36
|
+
|
37
|
+
Open `examples/demo-extension.kumi` in the debug window.
|
38
|
+
|
39
|
+
Position cursor after `fn(:` and type to trigger autocomplete:
|
40
|
+
|
41
|
+
```kumi
|
42
|
+
# Example 1: Basic arithmetic
|
43
|
+
let :sum, fn(:add, x, y)
|
44
|
+
↑
|
45
|
+
Type here and wait for suggestions
|
46
|
+
```
|
47
|
+
|
48
|
+
**Expected behavior:**
|
49
|
+
- Autocomplete shows `add`, `sub`, `mul`, `div`, etc.
|
50
|
+
- Each suggestion shows arity and function ID
|
51
|
+
- Press Escape to close, or select with Enter
|
52
|
+
|
53
|
+
### 5. Test Hover Information
|
54
|
+
|
55
|
+
Hover over function names to see documentation:
|
56
|
+
|
57
|
+
```kumi
|
58
|
+
let :sum, fn(:sum, input.values.item.price)
|
59
|
+
↑
|
60
|
+
Hover here to see type info
|
61
|
+
```
|
62
|
+
|
63
|
+
**Expected behavior:**
|
64
|
+
- Popup shows:
|
65
|
+
- Function name: `agg.sum`
|
66
|
+
- Arity: `1`
|
67
|
+
- Type: `same as source_value`
|
68
|
+
- Parameters: `source_value`
|
69
|
+
- Kernels: `ruby: agg.sum:ruby:v1`
|
70
|
+
|
71
|
+
### 6. Test Different Function Types
|
72
|
+
|
73
|
+
Try these in the demo file:
|
74
|
+
|
75
|
+
**Functions with identity:**
|
76
|
+
```kumi
|
77
|
+
fn(:sum, ...) # Shows Inline: += $1
|
78
|
+
fn(:count, ...) # Shows Inline: += 1
|
79
|
+
fn(:any, ...) # Shows Inline: = $0 || $1
|
80
|
+
```
|
81
|
+
|
82
|
+
**Functions without identity:**
|
83
|
+
```kumi
|
84
|
+
fn(:min, ...) # No Inline, shows note about first element
|
85
|
+
fn(:max, ...) # No Inline, shows note about first element
|
86
|
+
```
|
87
|
+
|
88
|
+
**Functions with multiple aliases:**
|
89
|
+
```kumi
|
90
|
+
fn(:add, ...) # Has alias: add
|
91
|
+
fn(:mul, ...) # Has aliases: mul, multiply
|
92
|
+
fn(:sum_if, ...) # Complex aggregation
|
93
|
+
```
|
94
|
+
|
95
|
+
### 7. Watch for Recompilation
|
96
|
+
|
97
|
+
In the debug window, TypeScript changes auto-compile:
|
98
|
+
|
99
|
+
```bash
|
100
|
+
npm run watch
|
101
|
+
```
|
102
|
+
|
103
|
+
Make a change to `src/extension.ts`, save, and reload the debug window (Cmd+R / Ctrl+R) to see changes.
|
104
|
+
|
105
|
+
## Troubleshooting
|
106
|
+
|
107
|
+
### Extension doesn't load
|
108
|
+
|
109
|
+
Check the Debug Console for errors:
|
110
|
+
- `Cmd+Shift+J` (Mac) or `Ctrl+Shift+J` (Linux/Windows)
|
111
|
+
|
112
|
+
### No autocomplete suggestions
|
113
|
+
|
114
|
+
1. Verify `docs/functions-reference.json` exists
|
115
|
+
2. Check extension loaded: Look for "Kumi functions reference loaded" in Debug Console
|
116
|
+
3. Make sure cursor is after `fn(:`
|
117
|
+
|
118
|
+
### JSON loading errors
|
119
|
+
|
120
|
+
If you see "Could not find functions-reference.json":
|
121
|
+
```bash
|
122
|
+
# Regenerate the JSON
|
123
|
+
bin/kumi-doc-gen
|
124
|
+
```
|
125
|
+
|
126
|
+
### Type suggestions not showing
|
127
|
+
|
128
|
+
1. Ensure you're in a `.kumi` or `.rb` file
|
129
|
+
2. Check the file language is recognized (bottom-right of editor shows language)
|
130
|
+
3. Try clicking on a function name and pressing `Cmd+K Cmd+I` to force hover
|
131
|
+
|
132
|
+
## File Locations
|
133
|
+
|
134
|
+
- Extension code: `vscode-extension/src/extension.ts`
|
135
|
+
- Function data: `docs/functions-reference.json`
|
136
|
+
- Demo file: `examples/demo-extension.kumi`
|
137
|
+
- VSCode config: `vscode-extension/package.json`
|
138
|
+
|
139
|
+
## Testing on Different File Types
|
140
|
+
|
141
|
+
### Kumi Files (.kumi)
|
142
|
+
```kumi
|
143
|
+
fn(:add, x, y) # Autocomplete and hover work
|
144
|
+
```
|
145
|
+
|
146
|
+
### Ruby Files (.rb)
|
147
|
+
```ruby
|
148
|
+
fn(:add, x, y) # Also works if inside Kumi schema
|
149
|
+
```
|
150
|
+
|
151
|
+
Both file types activate the extension and provide completions/hover.
|
@@ -0,0 +1,51 @@
|
|
1
|
+
{
|
2
|
+
"name": "kumi-language-support",
|
3
|
+
"displayName": "Kumi Language Support",
|
4
|
+
"description": "Language support for Kumi DSL with autocomplete and hover information",
|
5
|
+
"version": "0.1.0",
|
6
|
+
"publisher": "kumi",
|
7
|
+
"engines": {
|
8
|
+
"vscode": "^1.80.0"
|
9
|
+
},
|
10
|
+
"categories": [
|
11
|
+
"Programming Languages",
|
12
|
+
"Snippets"
|
13
|
+
],
|
14
|
+
"activationEvents": [
|
15
|
+
"onLanguage:ruby",
|
16
|
+
"onLanguage:kumi"
|
17
|
+
],
|
18
|
+
"main": "./out/extension.js",
|
19
|
+
"contributes": {
|
20
|
+
"languages": [
|
21
|
+
{
|
22
|
+
"id": "kumi",
|
23
|
+
"aliases": [
|
24
|
+
"Kumi",
|
25
|
+
"kumi"
|
26
|
+
],
|
27
|
+
"extensions": [
|
28
|
+
".kumi"
|
29
|
+
],
|
30
|
+
"configuration": "./language-configuration.json"
|
31
|
+
}
|
32
|
+
],
|
33
|
+
"grammars": [
|
34
|
+
{
|
35
|
+
"language": "kumi",
|
36
|
+
"scopeName": "source.kumi",
|
37
|
+
"path": "./syntaxes/kumi.tmLanguage.json"
|
38
|
+
}
|
39
|
+
]
|
40
|
+
},
|
41
|
+
"scripts": {
|
42
|
+
"vscode:prepublish": "npm run compile",
|
43
|
+
"compile": "tsc -p ./",
|
44
|
+
"watch": "tsc -watch -p ./"
|
45
|
+
},
|
46
|
+
"devDependencies": {
|
47
|
+
"@types/node": "^20.0.0",
|
48
|
+
"@types/vscode": "^1.80.0",
|
49
|
+
"typescript": "^5.0.0"
|
50
|
+
}
|
51
|
+
}
|
@@ -0,0 +1,295 @@
|
|
1
|
+
import * as vscode from 'vscode';
|
2
|
+
import * as fs from 'fs';
|
3
|
+
import * as path from 'path';
|
4
|
+
|
5
|
+
interface FunctionDef {
|
6
|
+
id: string;
|
7
|
+
arity: number;
|
8
|
+
params: Array<{ name: string }>;
|
9
|
+
dtype: any;
|
10
|
+
kernels: Record<string, string>;
|
11
|
+
aliases: string[];
|
12
|
+
kind?: string;
|
13
|
+
}
|
14
|
+
|
15
|
+
let functionsData: Record<string, FunctionDef> = {};
|
16
|
+
|
17
|
+
export function activate(context: vscode.ExtensionContext) {
|
18
|
+
// Load functions reference JSON from kumi/docs/
|
19
|
+
const refPath = path.join(
|
20
|
+
context.extensionPath,
|
21
|
+
'..',
|
22
|
+
'..',
|
23
|
+
'docs',
|
24
|
+
'functions-reference.json'
|
25
|
+
);
|
26
|
+
|
27
|
+
try {
|
28
|
+
if (fs.existsSync(refPath)) {
|
29
|
+
const content = fs.readFileSync(refPath, 'utf-8');
|
30
|
+
functionsData = JSON.parse(content);
|
31
|
+
console.log('Kumi functions reference loaded');
|
32
|
+
} else {
|
33
|
+
vscode.window.showWarningMessage(
|
34
|
+
'Kumi: functions-reference.json not found. Run "bin/kumi-doc-gen" to generate it.'
|
35
|
+
);
|
36
|
+
}
|
37
|
+
} catch (e) {
|
38
|
+
console.error('Failed to load functions reference:', e);
|
39
|
+
vscode.window.showErrorMessage(
|
40
|
+
'Kumi: Error loading functions reference: ' + String(e)
|
41
|
+
);
|
42
|
+
}
|
43
|
+
|
44
|
+
// Register completion provider for both Ruby and Kumi files
|
45
|
+
const completionProvider = vscode.languages.registerCompletionItemProvider(
|
46
|
+
[
|
47
|
+
{ language: 'ruby', scheme: 'file' },
|
48
|
+
{ language: 'kumi', scheme: 'file' }
|
49
|
+
],
|
50
|
+
new FunctionCompletionProvider(),
|
51
|
+
':' // trigger on ':'
|
52
|
+
);
|
53
|
+
|
54
|
+
// Register hover provider for both Ruby and Kumi files
|
55
|
+
const hoverProvider = vscode.languages.registerHoverProvider(
|
56
|
+
[
|
57
|
+
{ language: 'ruby', scheme: 'file' },
|
58
|
+
{ language: 'kumi', scheme: 'file' }
|
59
|
+
],
|
60
|
+
new FunctionHoverProvider()
|
61
|
+
);
|
62
|
+
|
63
|
+
context.subscriptions.push(completionProvider, hoverProvider);
|
64
|
+
}
|
65
|
+
|
66
|
+
class FunctionCompletionProvider implements vscode.CompletionItemProvider {
|
67
|
+
provideCompletionItems(
|
68
|
+
document: vscode.TextDocument,
|
69
|
+
position: vscode.Position,
|
70
|
+
token: vscode.CancellationToken,
|
71
|
+
context: vscode.CompletionContext
|
72
|
+
): vscode.CompletionItem[] {
|
73
|
+
// Check if we're in an fn(:...) context
|
74
|
+
const lineText = document.lineAt(position).text;
|
75
|
+
const beforeCursor = lineText.substring(0, position.character);
|
76
|
+
|
77
|
+
if (!beforeCursor.match(/fn\(\s*:\s*$/)) {
|
78
|
+
return [];
|
79
|
+
}
|
80
|
+
|
81
|
+
// For Ruby files, check if we're inside a schema block
|
82
|
+
if (document.languageId === 'ruby') {
|
83
|
+
if (!this.isInSchemaBlock(document, position)) {
|
84
|
+
return [];
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
const completions: vscode.CompletionItem[] = [];
|
89
|
+
|
90
|
+
for (const [alias, funcDef] of Object.entries(functionsData)) {
|
91
|
+
const item = new vscode.CompletionItem(
|
92
|
+
alias,
|
93
|
+
vscode.CompletionItemKind.Function
|
94
|
+
);
|
95
|
+
|
96
|
+
item.detail = `${funcDef.id} (arity: ${funcDef.arity})`;
|
97
|
+
item.documentation = new vscode.MarkdownString(
|
98
|
+
this.getDocumentation(funcDef)
|
99
|
+
);
|
100
|
+
item.sortText = alias;
|
101
|
+
|
102
|
+
completions.push(item);
|
103
|
+
}
|
104
|
+
|
105
|
+
return completions;
|
106
|
+
}
|
107
|
+
|
108
|
+
private getDocumentation(funcDef: FunctionDef): string {
|
109
|
+
const lines: string[] = [
|
110
|
+
`**${funcDef.id}**`,
|
111
|
+
'',
|
112
|
+
`**Arity:** ${funcDef.arity}`,
|
113
|
+
];
|
114
|
+
|
115
|
+
if (funcDef.params && funcDef.params.length > 0) {
|
116
|
+
lines.push('**Parameters:**');
|
117
|
+
funcDef.params.forEach((p) => {
|
118
|
+
lines.push(`- \`${p.name}\``);
|
119
|
+
});
|
120
|
+
}
|
121
|
+
|
122
|
+
if (funcDef.dtype) {
|
123
|
+
lines.push(`**Type:** ${this.formatType(funcDef.dtype)}`);
|
124
|
+
}
|
125
|
+
|
126
|
+
if (funcDef.kernels && Object.keys(funcDef.kernels).length > 0) {
|
127
|
+
lines.push('**Kernels:**');
|
128
|
+
Object.entries(funcDef.kernels).forEach(([target, id]) => {
|
129
|
+
lines.push(`- ${target}: \`${id}\``);
|
130
|
+
});
|
131
|
+
}
|
132
|
+
|
133
|
+
return lines.join('\n');
|
134
|
+
}
|
135
|
+
|
136
|
+
private formatType(dtype: any): string {
|
137
|
+
if (!dtype) return 'unknown';
|
138
|
+
|
139
|
+
switch (dtype.rule) {
|
140
|
+
case 'same_as':
|
141
|
+
return `same as \`${dtype.param}\``;
|
142
|
+
case 'scalar':
|
143
|
+
return dtype.kind || 'scalar';
|
144
|
+
case 'promote':
|
145
|
+
return `promoted from ${dtype.params.map((p: string) => `\`${p}\``).join(', ')}`;
|
146
|
+
case 'element_of':
|
147
|
+
return `element of \`${dtype.param}\``;
|
148
|
+
default:
|
149
|
+
return dtype.rule;
|
150
|
+
}
|
151
|
+
}
|
152
|
+
|
153
|
+
private isInSchemaBlock(document: vscode.TextDocument, position: vscode.Position): boolean {
|
154
|
+
let braceCount = 0;
|
155
|
+
let schemaFound = false;
|
156
|
+
|
157
|
+
// Search backwards from current position to find schema block
|
158
|
+
for (let i = position.line; i >= 0; i--) {
|
159
|
+
const line = document.lineAt(i).text;
|
160
|
+
|
161
|
+
// Count braces from end of line backwards
|
162
|
+
if (i === position.line) {
|
163
|
+
// Only count braces before cursor
|
164
|
+
for (let j = position.character - 1; j >= 0; j--) {
|
165
|
+
if (line[j] === '}') braceCount++;
|
166
|
+
if (line[j] === '{') braceCount--;
|
167
|
+
}
|
168
|
+
} else {
|
169
|
+
// Count all braces in the line (right to left)
|
170
|
+
for (let j = line.length - 1; j >= 0; j--) {
|
171
|
+
if (line[j] === '}') braceCount++;
|
172
|
+
if (line[j] === '{') braceCount--;
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
// Look for 'schema do' or 'schema {'
|
177
|
+
if (line.match(/\bschema\s+(do|{)/)) {
|
178
|
+
schemaFound = braceCount <= 0;
|
179
|
+
break;
|
180
|
+
}
|
181
|
+
}
|
182
|
+
|
183
|
+
return schemaFound;
|
184
|
+
}
|
185
|
+
}
|
186
|
+
|
187
|
+
class FunctionHoverProvider implements vscode.HoverProvider {
|
188
|
+
provideHover(
|
189
|
+
document: vscode.TextDocument,
|
190
|
+
position: vscode.Position,
|
191
|
+
token: vscode.CancellationToken
|
192
|
+
): vscode.ProviderResult<vscode.Hover> {
|
193
|
+
const range = document.getWordRangeAtPosition(position);
|
194
|
+
if (!range) return null;
|
195
|
+
|
196
|
+
const word = document.getText(range);
|
197
|
+
|
198
|
+
// Check if we're in fn(:word, ...)
|
199
|
+
const lineText = document.lineAt(position).text;
|
200
|
+
if (!lineText.match(/fn\s*\(\s*:/)) {
|
201
|
+
return null;
|
202
|
+
}
|
203
|
+
|
204
|
+
// For Ruby files, check if we're inside a schema block
|
205
|
+
if (document.languageId === 'ruby') {
|
206
|
+
if (!this.isInSchemaBlock(document, position)) {
|
207
|
+
return null;
|
208
|
+
}
|
209
|
+
}
|
210
|
+
|
211
|
+
const funcDef = functionsData[word];
|
212
|
+
if (!funcDef) {
|
213
|
+
return null;
|
214
|
+
}
|
215
|
+
|
216
|
+
const markdown = new vscode.MarkdownString();
|
217
|
+
markdown.appendMarkdown(`### \`${funcDef.id}\`\n\n`);
|
218
|
+
|
219
|
+
if (funcDef.aliases && funcDef.aliases.length > 0) {
|
220
|
+
markdown.appendMarkdown(
|
221
|
+
`**Aliases:** ${funcDef.aliases.map((a) => `\`${a}\``).join(', ')}\n\n`
|
222
|
+
);
|
223
|
+
}
|
224
|
+
|
225
|
+
markdown.appendMarkdown(`**Arity:** ${funcDef.arity}\n\n`);
|
226
|
+
|
227
|
+
if (funcDef.dtype) {
|
228
|
+
markdown.appendMarkdown(`**Type:** ${this.formatType(funcDef.dtype)}\n\n`);
|
229
|
+
}
|
230
|
+
|
231
|
+
if (funcDef.params && funcDef.params.length > 0) {
|
232
|
+
markdown.appendMarkdown('**Parameters:**\n\n');
|
233
|
+
funcDef.params.forEach((p) => {
|
234
|
+
markdown.appendMarkdown(`- \`${p.name}\`\n`);
|
235
|
+
});
|
236
|
+
markdown.appendMarkdown('\n');
|
237
|
+
}
|
238
|
+
|
239
|
+
return new vscode.Hover(markdown);
|
240
|
+
}
|
241
|
+
|
242
|
+
private formatType(dtype: any): string {
|
243
|
+
if (!dtype) return 'unknown';
|
244
|
+
|
245
|
+
switch (dtype.rule) {
|
246
|
+
case 'same_as':
|
247
|
+
return `same as \`${dtype.param}\``;
|
248
|
+
case 'scalar':
|
249
|
+
return dtype.kind || 'scalar';
|
250
|
+
case 'promote':
|
251
|
+
return `promoted from ${dtype.params
|
252
|
+
.map((p: string) => `\`${p}\``)
|
253
|
+
.join(', ')}`;
|
254
|
+
case 'element_of':
|
255
|
+
return `element of \`${dtype.param}\``;
|
256
|
+
default:
|
257
|
+
return dtype.rule;
|
258
|
+
}
|
259
|
+
}
|
260
|
+
|
261
|
+
private isInSchemaBlock(document: vscode.TextDocument, position: vscode.Position): boolean {
|
262
|
+
let braceCount = 0;
|
263
|
+
let schemaFound = false;
|
264
|
+
|
265
|
+
// Search backwards from current position to find schema block
|
266
|
+
for (let i = position.line; i >= 0; i--) {
|
267
|
+
const line = document.lineAt(i).text;
|
268
|
+
|
269
|
+
// Count braces from end of line backwards
|
270
|
+
if (i === position.line) {
|
271
|
+
// Only count braces before cursor
|
272
|
+
for (let j = position.character - 1; j >= 0; j--) {
|
273
|
+
if (line[j] === '}') braceCount++;
|
274
|
+
if (line[j] === '{') braceCount--;
|
275
|
+
}
|
276
|
+
} else {
|
277
|
+
// Count all braces in the line (right to left)
|
278
|
+
for (let j = line.length - 1; j >= 0; j--) {
|
279
|
+
if (line[j] === '}') braceCount++;
|
280
|
+
if (line[j] === '{') braceCount--;
|
281
|
+
}
|
282
|
+
}
|
283
|
+
|
284
|
+
// Look for 'schema do' or 'schema {'
|
285
|
+
if (line.match(/\bschema\s+(do|{)/)) {
|
286
|
+
schemaFound = braceCount <= 0;
|
287
|
+
break;
|
288
|
+
}
|
289
|
+
}
|
290
|
+
|
291
|
+
return schemaFound;
|
292
|
+
}
|
293
|
+
}
|
294
|
+
|
295
|
+
export function deactivate() {}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
{
|
2
|
+
"compilerOptions": {
|
3
|
+
"module": "commonjs",
|
4
|
+
"target": "ES2020",
|
5
|
+
"lib": ["ES2020"],
|
6
|
+
"outDir": "./out",
|
7
|
+
"rootDir": "./src",
|
8
|
+
"strict": true,
|
9
|
+
"esModuleInterop": true,
|
10
|
+
"skipLibCheck": true,
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
12
|
+
"resolveJsonModule": true
|
13
|
+
},
|
14
|
+
"exclude": ["node_modules", ".vscode-test"]
|
15
|
+
}
|