Skip to content

Commit 7910bee

Browse files
authored
Consider the content of the new cells during notebook sync (#12203)
## Summary This PR fixes the bug where the server was not considering the `cells.structure.didOpen` field to sync up the new content of the newly added cells. The parameters corresponding to this request provides two fields to get the newly added cells: 1. `cells.structure.array.cells`: This is a list of `NotebookCell` which doesn't contain any cell content. The only useful information from this array is the cell kind and the cell document URI which we use to initialize the new cell in the index. 2. `cells.structure.didOpen`: This is a list of `TextDocumentItem` which corresponds to the newly added cells. This actually contains the text content and the version. This wasn't a problem before because we initialize the cell with an empty string and this isn't a problem when someone just creates an empty cell. But, when someone copy-pastes a cell, the cell needs to be initialized with the content. fixes: #12201 ## Test Plan First, let's see the panic in action: 1. Press <kbd>Esc</kbd> to allow using the keyboard to perform cell actions (move around, copy, paste, etc.) 2. Copy the second cell with <kbd>c</kbd> key 3. Delete the second cell with <kbd>dd</kbd> key 4. Paste the copied cell with <kbd>p</kbd> key You can see that the content isn't synced up because the `unused-import` for `sys` is still being highlighted but it's being used in the second cell. And, the hover isn't working either. Then, as I start editing the second cell, it panics. https://github.com/astral-sh/ruff/assets/67177269/fc58364c-c8fc-4c11-a917-71b6dd90c1ef Now, here's the preview of the fixed version: https://github.com/astral-sh/ruff/assets/67177269/207872dd-dca6-49ee-8b6e-80435c7ef22e
1 parent f3ccd15 commit 7910bee

File tree

2 files changed

+35
-7
lines changed

2 files changed

+35
-7
lines changed

crates/ruff_server/src/edit/notebook.rs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,31 +109,55 @@ impl NotebookDocument {
109109
text_content,
110110
}) = cells
111111
{
112+
// The structural changes should be done first, as they may affect the cell index.
112113
if let Some(structure) = structure {
113114
let start = structure.array.start as usize;
114115
let delete = structure.array.delete_count as usize;
116+
117+
// First, delete the cells and remove them from the index.
115118
if delete > 0 {
116119
for cell in self.cells.drain(start..start + delete) {
117120
self.cell_index.remove(&cell.url);
118121
}
119122
}
123+
124+
// Second, insert the new cells with the available information. This array does not
125+
// provide the actual contents of the cells, so we'll initialize them with empty
126+
// contents.
120127
for cell in structure.array.cells.into_iter().flatten().rev() {
121128
self.cells
122-
.insert(start, NotebookCell::new(cell, String::new(), version));
129+
.insert(start, NotebookCell::new(cell, String::new(), 0));
123130
}
124131

125-
// register any new cells in the index and update existing ones that came after the insertion
126-
for (i, cell) in self.cells.iter().enumerate().skip(start) {
127-
self.cell_index.insert(cell.url.clone(), i);
132+
// Third, register the new cells in the index and update existing ones that came
133+
// after the insertion.
134+
for (index, cell) in self.cells.iter().enumerate().skip(start) {
135+
self.cell_index.insert(cell.url.clone(), index);
136+
}
137+
138+
// Finally, update the text document that represents the cell with the actual
139+
// contents. This should be done at the end so that both the `cells` and
140+
// `cell_index` are updated before we start applying the changes to the cells.
141+
if let Some(did_open) = structure.did_open {
142+
for cell_text_document in did_open {
143+
if let Some(cell) = self.cell_by_uri_mut(&cell_text_document.uri) {
144+
cell.document = TextDocument::new(
145+
cell_text_document.text,
146+
cell_text_document.version,
147+
);
148+
}
149+
}
128150
}
129151
}
152+
130153
if let Some(cell_data) = data {
131154
for cell in cell_data {
132155
if let Some(existing_cell) = self.cell_by_uri_mut(&cell.document) {
133156
existing_cell.kind = cell.kind;
134157
}
135158
}
136159
}
160+
137161
if let Some(content_changes) = text_content {
138162
for content_change in content_changes {
139163
if let Some(cell) = self.cell_by_uri_mut(&content_change.document.uri) {
@@ -143,9 +167,11 @@ impl NotebookDocument {
143167
}
144168
}
145169
}
170+
146171
if let Some(metadata_change) = metadata_change {
147172
self.metadata = serde_json::from_value(serde_json::Value::Object(metadata_change))?;
148173
}
174+
149175
Ok(())
150176
}
151177

crates/ruff_server/src/session/index.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,14 +145,16 @@ impl Index {
145145
encoding: PositionEncoding,
146146
) -> crate::Result<()> {
147147
// update notebook cell index
148-
if let Some(lsp_types::NotebookDocumentCellChangeStructure { did_open, .. }) =
149-
cells.as_ref().and_then(|cells| cells.structure.as_ref())
148+
if let Some(lsp_types::NotebookDocumentCellChangeStructure {
149+
did_open: Some(did_open),
150+
..
151+
}) = cells.as_ref().and_then(|cells| cells.structure.as_ref())
150152
{
151153
let Some(path) = self.url_for_key(key).cloned() else {
152154
anyhow::bail!("Tried to open unavailable document `{key}`");
153155
};
154156

155-
for opened_cell in did_open.iter().flatten() {
157+
for opened_cell in did_open {
156158
self.notebook_cells
157159
.insert(opened_cell.uri.clone(), path.clone());
158160
}

0 commit comments

Comments
 (0)