Skip to content

Map LSP documents by normalised FilePath#1252

Merged
lionel- merged 2 commits into
mainfrom
oak/salsa-16-document-wire-url
Jun 19, 2026
Merged

Map LSP documents by normalised FilePath#1252
lionel- merged 2 commits into
mainfrom
oak/salsa-16-document-wire-url

Conversation

@lionel-

@lionel- lionel- commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Branched from #1251
Progress towards #1212

See #1250 for more context.

  • WorldState now keys documents by normalised FilePath instead of Url. See state.rs. Keying by normalised paths will prevent bugs when matching to paths that don't come from the frontend, e.g. source() paths in user code.

  • The original Url sent by the LSP frontend is stored in Document. This is used in communications with the frontend so that we send back exact byte-for-byte Urls, preventing any file matching bugs that could be caused by our internal normalisation of file paths.

Unlike the DAP which has to deal with paths normalised by R, we never resolve symlinks on the LSP side. All matching is done on FilePath based on pure lexical normalisation of paths.

@lionel- lionel- force-pushed the oak/salsa-16-document-wire-url branch from 10e2102 to f6332d0 Compare June 3, 2026 12:12
@lionel- lionel- force-pushed the oak/salsa-15-file-path-refactor branch from 4ac9528 to e807fd2 Compare June 4, 2026 14:19
@lionel- lionel- force-pushed the oak/salsa-16-document-wire-url branch from f6332d0 to 14bacca Compare June 4, 2026 14:19

@DavisVaughan DavisVaughan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I really like the idea of this PR! But one blocking comment about Document that I'd like to resolve first

Comment thread crates/ark/src/lsp/state.rs Outdated
Comment on lines 84 to 93
pub(crate) fn get_document(&self, uri: &Url) -> anyhow::Result<&Document> {
if let Some(doc) = self.documents.get(uri) {
let key = FilePath::from_url(uri);
if let Some(doc) = self.documents.get(&key) {
Ok(doc)
} else {
Err(anyhow!("Can't find document for URI {uri}"))
}
}

pub(crate) fn get_document_mut(&mut self, uri: &Url) -> anyhow::Result<&mut Document> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I feel like these should take path: &FilePath. It would encourage conversion to FilePath at the LSP boundary

Comment on lines +19 to +22
/// Watched documents, keyed on the normalised [`FilePath`] form.
/// The verbatim editor URL is preserved on each [`Document::url`]
/// for wire output.
pub(crate) documents: HashMap<FilePath, Document>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I would like to advocate for the following strategy

pub(crate) documents: HashMap<FilePath, (Url, Document)>,
  • Document no longer knows about url. I think this is correct, as you should be able to create a Document without a corresponding url.
  • Document no longer has that weird PLACEHOLDER_URL hack, which i really do not like
  • insert_document() would be removed since we only .insert() in 1 place in production code, and then in a few tests, so the abstraction doesn't feel worth it

WorldState::get_document() and WorldState::get_document_mut() would then:

  • Take a path: FilePath as I mentioned elsewhere
  • Abstract over the (Url, Document) enum and just pull out the Document and return it

We would need to go update 2 direct usages of state.document.get() to be state.get_document(), but I think we should do that anyways.

This feels much much better to me, and I looked at all call sites of state.documents. and think it would work quite well

I am quite strongly against PLACEHOLDER_URL, so am going to block this pr until that is resolved

@lionel- lionel- Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

oops sorry, my fault. I asked Claude to get rid of Option and didn't closely review that one. The placeholder is not only unsightly, it would leak to the user if we made a mistake somewhere.

I did consider hoisting url out of there but it conflicted with existing code IIRC, and I wanted to avoid making too many changes to legacy Ark handlers. Let me take another look.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

oh wait I forgot it's already gone in an ulterior PR!

@lionel- lionel- force-pushed the oak/salsa-15-file-path-refactor branch from e807fd2 to 10deebe Compare June 10, 2026 14:32
@lionel- lionel- force-pushed the oak/salsa-16-document-wire-url branch from 14bacca to 7743983 Compare June 10, 2026 14:32
@lionel- lionel- force-pushed the oak/salsa-15-file-path-refactor branch 2 times, most recently from 26ed4dd to 11deb77 Compare June 11, 2026 09:40
@lionel- lionel- force-pushed the oak/salsa-16-document-wire-url branch from 7743983 to a9b0bf2 Compare June 11, 2026 09:42
@lionel- lionel- force-pushed the oak/salsa-15-file-path-refactor branch from 11deb77 to 1c94827 Compare June 19, 2026 13:49
Base automatically changed from oak/salsa-15-file-path-refactor to main June 19, 2026 13:49
@lionel- lionel- force-pushed the oak/salsa-16-document-wire-url branch from c4a0c66 to 58bcce0 Compare June 19, 2026 13:50
@lionel- lionel- merged commit 1a36c0d into main Jun 19, 2026
17 checks passed
@lionel- lionel- deleted the oak/salsa-16-document-wire-url branch June 19, 2026 13:50
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 19, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants