A field day for Gleam's language server – Gleam v1.18.0 release

linggen1 pts0 comments

A field day for Gleam’s language server | Gleam programming languageA field day for Gleam’s language server<br>Gleam v1.18.0 released!

29 July, 2026 by Louis Pilfold<br>ShareCopied the post URL!<br>Gleam is a type safe and scalable language for the Erlang virtual machine and<br>JavaScript runtimes. Today Gleam v1.18.0 has been published.

Language server record field support

Gleam's focus on static analysis and static types really helps with creating<br>tooling, and that can be seen very clearly with Gleam's much-loved language<br>server. All its features and capabilities can be seen in<br>its documentation.

One remaining missing part of it was full support for record fields, a problem<br>now fixed! The language server now supports go-to-definition, find-references<br>and rename for record fields. These work on the field declaration, on labelled<br>arguments, on labelled patterns, on record updates and on record.field<br>accesses, both within a module and across modules. For example:

pub type Person {<br>Person(name: String, age: Int)

pub fn main() {<br>let lucy = Person(name: "Lucy", age: 10)<br>lucy.name<br>// ^ Go-to-definition jumps to the `name` field, and renaming it here<br>// renames the field everywhere it is used.

Thank you Alistair Smith! People will be very happy<br>for this one!

Type variable renaming

Similarly, the language server now permits renaming type variables in<br>functions, types, and constants. For example:

pub fn twice(value: a, f: fn(a) -> a) -> a {<br>// ^ Rename to "anything"<br>f(f(value))

Produces:

pub fn twice(value: anything, f: fn(anything) -> anything) -> anything {<br>f(f(value))

Thank you Surya Rose!

Updating imports for renamed modules

Another addition to the language server is support for renaming modules. When<br>your text editor tells the language server that a Gleam file has been renamed,<br>it will find all the uses of that module and update them for the new name. For<br>example:

import db_users

pub fn main() -> db_users.User {<br>db_users.new("username")

Renaming db_users.gleam to database/user.gleam would produce:

import database/user

pub fn main() -> user.User {<br>user.new("username")

Thank you Surya Rose!

Pattern match on value code action

Flow control in Gleam is done with pattern matching. With how common this is,<br>the language server's "pattern match on value" code action is a great<br>time-saver, quickly inserting a case expression that the programmer can add<br>their logic to.

The code action can now be triggered on function calls and their returned<br>values. For example:

pub fn main() {<br>load_user()<br>// ^^ Triggering the code action over here

fn load_user() -> Result(User, Nil) { todo }

Will produce the following code:

pub fn main() {<br>case load_user() {<br>Ok(value) -> todo<br>Error(value) -> todo

Thank you Giacomo Cavalieri!

Faster JavaScript with data singletons

When targeting JavaScript, Gleam's compiler outputs straightforward JavaScript<br>code, similar to what a human would write. This gives it performance similar to<br>hand-written JavaScript, which the web framework Lustre has used to achieve<br>comparable or better performance to JavaScript frameworks, such as React.

That said, there is always opportunity to improve the code generated by a<br>compiler. Gleam will now identify data structures where all instances are<br>equivalent, and use a single value for each use instead of constructing a new<br>instance each time.

Take this Gleam code, for example:

import gleam/option.{type Option}

pub fn two_optionals() -> #(Option(a), Option(b)) {<br>#(option.None, option.None)

You could imagine that previously, that Gleam code would compile to JavaScript code<br>like this (pseudocode):

import { None } "../gleam_stdlib/gleam/option.mjs";

export function two_optionals() {<br>return [None.new(), None.new()];

While now code closer to this will be generated (pseudocode):

import { None } "../gleam_stdlib/gleam/option.mjs";

export function two_optionals() {<br>return [None.instance, None.instance];

In our tests this has an impactful improvement to performance, especially to<br>code that uses lots of these data structures such as the view functions of<br>Lustre applications.

Thank you Surya Rose for this<br>optimisation!

Ambiguous pipe syntax deprecation

Gleam's much-loved pipe syntax gives programmers another way to write nested<br>function calls so that they read top-to-bottom and left-to-right.

The pipeline one |> two is equivalent to two(one). Most of the time one |> two(three)<br>is equivalent to two(one, three), however if that doesn't result in the<br>correct types then it can instead compile to two(three)(one). This second<br>option means that the pipe syntax is visually ambiguous, and one cannot tell<br>exactly how it compiles without knowing about the function being called. This<br>is not in keeping with the wider language, so the two(three)(one) version has<br>been deprecated and will need to be written explicitly as one |> two(three)().<br>With this, pipelines have the same rules as use expressions, and the reader<br>can always tell how they compile.

We scanned all...

gleam language code server value option

Related Articles