Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>RagaNew to Visual Studio Code? Get it now.
Raga

Raga

Raga

| (0) | Free
Language support for the Raga programming language (.raga): type inference, completion, signature help, go to definition, rename, outline and inlay hints
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Raga

Language support for Raga (.raga): highlighting, snippets, and an analyzer that infers types and helps while you type.

Raga is a language design, not an implementation: the analyzer checks code against the design rules, it does not compile or run it.

What it does

  • Type inference for primitives, non-primitive values, generics, collections, pointers, Optional and Result, functions, methods, callbacks and trailing blocks. Hover any name to see its type and its documentation comment.
  • Completion
    • after a dot: the fields, properties and methods of structs, classes (with inheritance, traits and interfaces), enums, tagged unions, String, List, Array, Map, Set, Tuple, Optional, Result, Weak, pointers and imported modules
    • variables, functions and types that are visible at the cursor, built-in functions, primitive types and keywords
    • the field names of a struct or class being created, and the parameter names of a call (Point(x = ...))
    • the variants after case (, the modifiers inside [method(...)] (Visibility., Inheritance., Ownership. and their members), and file paths inside import("...")
  • Classes: methods are const name = method(...) { } with overloads chained after the closing brace, fields are plain declarations (String name = ""), accessors are const marks = get { } set |value| { }, and [method(...)], [field(...)], [accessor(...)], [get(...)], [set(...)] modifiers are checked. The modifiers are members of three enums, not keywords: Visibility (Public, Protected, Private), Inheritance (Final, Abstract, Virtual, Override) and Ownership (Instance, Static), written like [method(Visibility.Private, Ownership.Static)]. A static member is reached on the class, a static method has no this and cannot use instance members.
  • Map: Map<K, V> takes two types, a hash table under the hood. A key is a primitive value or a String: a struct, class, collection, enum or union as the key is reported (key by a text or a number built from the fields). Map("ann", 31, "bob", 27) infers Map<String, i32>, and Map<i32> with one type is reported.
  • Errors: IError is the built-in interface (String message, i32 code) and Error is the built-in class that implements it, so Error(message = "boom", code = 2) is an ordinary creation and your own error is a class that implements IError or extends Error. throw takes only those, catch |e| gives an IError (e.message, e.code, e.is<T>(), e.as<T>()), and implements(Error) is reported ("Error is a class: write implements(IError) or extends(Error)").
  • break and continue take an optional number of loops to leave besides the innermost one: break is break 0, break 1 leaves two loops, continue 1 goes to the next pass of the loop around. The number is a whole number written in the code, and a number larger than the loops around, a negative one, and a name are reported.
  • Watching the disk: a change to raga.project, or to any .raga file outside the editor, is picked up without a reload. A generic call whose arguments must share a type (Math.max(1, 2.5)) and a block that names more values than its callback gives (or_else |why, more|) are reported.
  • Generic functions as values: identity<i32> is a function value of type function(i32) i32 that can be passed, kept and returned, a bare generic name is a value only where a function type is expected, and anywhere else it is reported.
  • Operators: [operator(Op.Add)] is checked on functions, struct methods and class methods: the member must exist in Op, and a binary operator takes two values (a function) or one besides the object (a method), a unary one takes one or none.
  • Trailing blocks: the block after a call is its last argument written in place. The type of the last parameter decides what it is: a function type takes a function body (|n| { return n }), an Array<T> takes its values ({ 1, 2, 3 }, each one checked against T), and a struct takes one name = value line per field (every field once, any order, each value checked against its field, a missing, unknown or repeated field is reported). Any other last parameter, a variadic one, one already given inside the parentheses (also when it was given by position), a |names| on an Array or struct block, and a creation such as Point(...) { } report an error that says where the value goes instead. The same holds for a clause of a chain (begin { } headers { "a", "b" }), because it is a call of a method. The declaration decides what { } is: a function with no parameters and a struct with no fields look the same, so the type of the last parameter is the only thing that tells them apart.
  • Element types of a collection literal: when a type is asked for (Array<i32>, List<String>, as a parameter, a declared variable or a trailing block), each element is checked against it, so tally("scores") { 'f' } reports char is not i32.
  • Trailing blocks and chains in completion: after the ) of a call whose last parameter is a callback, completion offers the block that fills it, with the names the callback gives (|result| { return }, and return when the callback gives a value); for an Array it offers { } and for a struct it offers a block with one line per field (name = , size = ); after a space it opens by itself. After the } of a block on the same line, it offers the next clauses of the chain, the methods of what the call gave back whose last parameter is a callback (on_error |error| { }, cleanup { }).
  • Attribute call hooks on methods: an attribute limited to methods with [on(Kind.Method)] has a call block with four parameters, |target, receiver, args, next|, and any other attribute has three. A wrong count is reported.
  • Library: the built-in types Math (rounding, trigonometry, Math.pi), System, File, Time and Random, the String methods index_of, get, slice, split, trim, replace, lower, String.format and $ with C format checks, and import that must be given a path or library name written in the code. Calls to built-in methods have their arguments checked.
  • Closures: a function can be defined anywhere, also inside another function or block, and it may use the locals, parameters and loop names around it. They are shared with the closure, not copied, so a let that a closure changes is changed for everyone, and hover marks such a name as "captured by a closure: lives on the heap while a closure uses it". Function values (function(i32) i32) can be returned, kept in variables, struct and class fields, Lists and Maps, compared with == and called right away with f()() or fns.get(0)(3). A trailing block is a closure too. The editor types them, checks the arguments of a stored function and the return types, and reports a name used before its declaration, an assignment to a captured const or parameter, and functions of different types in one return.
  • Attributes come in three patterns: metadata only (const note = attribute(String text), read with attributes_of), a build hook (... |target, text| { }, runs once per use while the program is built) and a call hook (} call |target, args, next| { return next(args) }, runs on every call). Block parameters are always named in the pipes (target first, then the declared parameters in order, or ...rest for an attribute without parameters), nothing is in scope by magic and the number of names is checked.
  • Calls without parentheses: after a function name the next token on the same line decides. { or |x| { is a trailing block with no argument outside it (run { }, repeat |i| { }, and show { x = 1 } for a function whose only parameter is a struct). A number (double 5, double -5), a char, true/false, or a name with its .members and calls (show user.name, double sum(xs), double sum xs) is the one argument. A String, List, Array, Tuple, null or !x is not a bare argument and reports a hint to use parentheses, and a call with a trailing block keeps its parentheses. f -x and f - x stay subtraction, f -5 is f(-5), and a name followed by an argument that is not a function reports "x is not a function, it cannot take an argument". A bare name alone is still the function value.
  • Signature help for functions, methods (every overload) and struct or class creation.
  • Struct and union fields with defaults (f64 x = 0.0, struct { f64 w = 1.0, f64 h = 1.0 }): a field without a default must be given when the value is created, Point() is valid when every field has one, and Bits32() starts a union as its first field when that field has a default. Hover, signature help and named-argument completion show the defaults.
  • Go to definition (also into other files and through import), find references, rename, outline.
  • Inlay hints: the inferred type after const and let names, loop names and the names of a trailing block.
  • Diagnostics while you type: undeclared names, unknown fields and methods, wrong argument counts, struct creation rules, assigning to a const, a literal of the wrong kind or too large for its type, null for a non-pointer, return outside a function, built-in names declared again, a type that holds itself directly, export(a, b) (a function, the last statement of a file) and import mistakes, and modules that import each other.

Naming style (snake_case, camelCase, Capitalized) is a convention and is never reported.

Settings

Setting Default
raga.inlayHints.enabled true show inferred types after names
raga.diagnostics.enabled true report mistakes while typing

How it is built

src/lexer.js and src/parser.js read the code into a tree and never fail on half-typed code. src/prelude.js describes the built-in types and functions in Raga itself (it follows raga/templates/11-standard-library.raga). src/analyzer.js binds names and infers types, src/queries.js answers the editor, src/workspace.js loads imported files. extension.js is the only file that uses the VS Code API.

  • Bodies hold definitions, not bare names. An enum member is const Red = 0 (always with an integer value, no two the same), a variant named from outside is const Square = Square, and members of a struct, enum or union are separated by a new line or ;, never a comma (struct { f64 w; f64 h }). The old forms report what to write instead.
  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft