Skip to content

LSP: cleanup when a client has closed all files in a project#5381

Open
TrippleCCC wants to merge 7 commits intogleam-lang:mainfrom
TrippleCCC:lsp-project-cleanup
Open

LSP: cleanup when a client has closed all files in a project#5381
TrippleCCC wants to merge 7 commits intogleam-lang:mainfrom
TrippleCCC:lsp-project-cleanup

Conversation

@TrippleCCC
Copy link

@TrippleCCC TrippleCCC commented Feb 21, 2026

Closes #3205

The language server now keeps track of open files and removes the project engine once all open project files have been closed.

The issue also mentions clearing all diagnostics; I believe this should already be the case since discard_in_memory_cache returns an empty feedback.

It looks like LanguageServer is not set up for testing. I would be happy to create tests for this if it is necessary.


  • The changes in this PR have been discussed beforehand in an issue
  • The issue for this PR has been linked
  • Tests have been added for new behavior
  • The changelog has been updated for any user-facing changes

@TrippleCCC TrippleCCC changed the title Lsp project cleanup LSP: cleanup when a client has closed all files in a project Feb 21, 2026
Copy link
Contributor

@ankddev ankddev left a comment

Choose a reason for hiding this comment

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

Hello! I've left some notes inline.

Also, it should clean all diagnsotic when I close all files in project, am I right? For me it doesn't work, I still see diagnostics (Zed):

Image

@TrippleCCC
Copy link
Author

Hello! I've left some notes inline.

Also, it should clean all diagnsotic when I close all files in project, am I right? For me it doesn't work, I still see diagnostics (Zed):

I initially miss understood the procedure for clearing diagnostics. The latest changes should fix this.

Copy link
Member

@lpil lpil left a comment

Choose a reason for hiding this comment

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

Great, thank you! I've left some comments inline.

There's no tests for this yet- how do we verify that it works now and in future?

The language server is quite complex, and it's not clear why things are done, so detailed comments explaining the what and the why of each bit of functionality would be very helpful too.

SourceFileChangedInMemory { path: Utf8PathBuf, text: String },
SourceFileChangedInMemory {
path: Utf8PathBuf,
opened: bool,
Copy link
Member

Choose a reason for hiding this comment

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

What does this represent, and why do we need to track it?

Could you add documentation please 🙏

Copy link
Author

Choose a reason for hiding this comment

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

Moved this into separate notification enums. Should be a bit more clear

let notification = Notification::SourceFileChangedInMemory {
path: super::path(&params.text_document.uri),
text: params.content_changes.into_iter().next_back()?.text,
opened: false,
Copy link
Member

Choose a reason for hiding this comment

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

When is it possible to have files edited in memory but not open? Isn't that impossible?

Copy link
Author

Choose a reason for hiding this comment

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

SourceFileChangedInMemory was used for both open and change notifications; the flag was used to diff between the two. I went ahead an separated like you suguested.

outside_of_project_feedback: FeedbackBookKeeper,
router: Router<IO, ConnectionProgressReporter<'a>>,
changed_projects: HashSet<Utf8PathBuf>,
opened_files: HashMap<Utf8PathBuf, bool>,
Copy link
Member

Choose a reason for hiding this comment

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

What does this represent?

Copy link
Author

Choose a reason for hiding this comment

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

Added a comment to explain a bit more. TLDR this allows us to keep diagnostics for closed files up when not all project files are open.

let feedback = self.cache_file_in_memory(&path, text);
if opened && feedback.diagnostics.is_empty() {
let _ = self.opened_files.insert(path, true);
}
Copy link
Member

Choose a reason for hiding this comment

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

How come we only record the file as opened if there's no diagnostics?

Copy link
Author

Choose a reason for hiding this comment

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

I removed this check. We actually should still track that the file is open even if we don't return diagnostics.

let _ = self
.opened_files
.get_mut(&path)
.map(|is_open| *is_open = false);
Copy link
Member

Choose a reason for hiding this comment

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

What's the difference between the value being absent and being false?

Copy link
Author

Choose a reason for hiding this comment

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

Added a comment to the opened_files field

@lpil lpil marked this pull request as draft February 25, 2026 18:10
@giacomocavalieri
Copy link
Member

Hello @TrippleCCC! If you think the PR is ready to be reviewed again remember to un-draft it

@TrippleCCC TrippleCCC marked this pull request as ready for review February 26, 2026 10:33
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.

LSP: cleanup when a client has closed all files in a project

4 participants