Skip to content

Add LSP completion for field numbers#4260

Merged
stefanvanburen merged 10 commits intomainfrom
svanburen/complete-next-available-field
Jan 6, 2026
Merged

Add LSP completion for field numbers#4260
stefanvanburen merged 10 commits intomainfrom
svanburen/complete-next-available-field

Conversation

@stefanvanburen
Copy link
Member

Suggests the next available field number, taking into account existing field numbers in the message as well as reserved field numbers. We want this so that people get suggested the next lowest field number, from protobuf.com:

When creating messages, authors should prefer lower numbers:
unmarshaling a message is typically optimized for smaller field
numbers. Also, smaller values are more efficiently encoded and decoded:
field numbers 1 to 15 can be encoded in a single byte. So it is
recommended to always start numbering fields at 1 and to use smaller
values for more commonly used fields when possible.

Should work with or without the semicolon after, e.g.:

message ABC {
  string xyz = |
}
message ABC {
  string xyz = |;
}

Suggests the next available field number, taking into account existing
field numbers in the message as well as reserved field numbers. We want
this so that people get suggested the next lowest field number, from
[protobuf.com][1]:

> When creating messages, authors should prefer lower numbers:
> unmarshaling a message is typically optimized for smaller field
> numbers. Also, smaller values are more efficiently encoded and decoded:
> field numbers 1 to 15 can be encoded in a single byte. So it is
> recommended to always start numbering fields at 1 and to use smaller
> values for more commonly used fields when possible.

Should work with or without the semicolon after, e.g.:

```proto
message ABC {
  string xyz = |
}
```

```proto
message ABC {
  string xyz = |;
}
```

[1]: https://protobuf.com/docs/language-spec#field-numbers
@github-actions
Copy link
Contributor

github-actions bot commented Jan 6, 2026

The latest Buf updates on your PR. Results from workflow Buf CI / buf (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedJan 6, 2026, 9:00 PM

}

// Check if the first non-whitespace token is an equals sign
return !before.IsZero() && before.Kind() == token.Keyword && before.Text() == "="
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can check this against keyword.Assign.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

36b9ae8. Probably don't even need to check if the token is a keyword?

}

func completionItemsForFieldNumber(
parentDef ast.DeclDef,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I know that completions are based off the AST, and this has to do with resolving arbitrary and potentially invalid locations in the token stream, but given that we have the parentDef here, an alternative approach we can take is resolving the parentDef's symbol, which would give us access to the ir.Symbol. And since the parentDef is asserted to be a valid message, the ir.Symbol should always be a ir.Type, which has a nice API for accessing the reserved ranges, without us re-doing the parsing here.

There might be a catch with this approach, but maybe it's worth looking into?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7d7ca07 - there might be a simpler way to resolve the parentDef's symbol, though?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like maybe not, so just consolidated the callsites into the same helper: c383ffe

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^ honestly, this is pretty much exactly what I had in mind, lol. it's a little ugly because you need to pass the file around, but i feel like it's not the worst? given that it's always going to be scoped to the current file, it's pretty safe?

Copy link
Member

@doriable doriable left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, I pulled in main and tested locally. Going to pre-approve, but do you mind putting the feature into the changelog?

@stefanvanburen
Copy link
Member Author

Looks good, I pulled in main and tested locally. Going to pre-approve, but do you mind putting the feature into the changelog?

Added in e3776f5; merging, but feel free to wordsmith separately!

@stefanvanburen stefanvanburen changed the title Add completion for field numbers Add LSP completion for field numbers Jan 6, 2026
@stefanvanburen stefanvanburen merged commit 5058bf9 into main Jan 6, 2026
11 checks passed
@stefanvanburen stefanvanburen deleted the svanburen/complete-next-available-field branch January 6, 2026 21:05
stefanvanburen added a commit that referenced this pull request Jan 15, 2026
Similar to how this works for messages, but with an enum-specific flair;
enum values must be an int32 (including negative values).

Also simplifies the CompletionItem to only include a Label; we don't
need to specify `InsertText` if it's the same value.

(We mentioned this feature specifically in [the LSP blog post][1] and
I realized I had totally forgotten about doing it alongside message
field completion in #4260.)

[1]: https://buf.build/blog/protobuf-lsp
stefanvanburen added a commit that referenced this pull request Jan 15, 2026
Similar to how this works for messages, but with an enum-specific flair;
enum values must be an int32 (including negative values).

Also simplifies the CompletionItem to only include a Label; we don't
need to specify `InsertText` if it's the same value.

(We mentioned this feature specifically in [the LSP blog post][1] and
I realized I had totally forgotten about doing it alongside message
field completion in #4260.)

[1]: https://buf.build/blog/protobuf-lsp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants