Skip to content

Commit 131344c

Browse files
committed
Fix #5284: Restore worksheet decorations
When switching tabs, VSCode discards the text decorations that were added by worksheet evaluation. We now keep them in memory so that they can be added again when coming back to the worksheet.
1 parent a821035 commit 131344c

File tree

1 file changed

+55
-10
lines changed

1 file changed

+55
-10
lines changed

vscode-dotty/src/worksheet.ts

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import {
55
} from 'vscode'
66

77
import {
8-
asWorksheetRunParams, WorksheetRunRequest, WorksheetRunParams, WorksheetRunResult,
8+
asWorksheetRunParams, WorksheetRunRequest, WorksheetRunResult,
99
WorksheetPublishOutputParams, WorksheetPublishOutputNotification
1010
} from './protocol'
11-
import { BaseLanguageClient, DocumentSelector } from 'vscode-languageclient'
11+
import { BaseLanguageClient } from 'vscode-languageclient'
1212
import { Disposable } from 'vscode-jsonrpc'
1313

1414
/**
@@ -23,11 +23,26 @@ export const worksheetRunKey = "dotty.worksheet.run"
2323
*/
2424
export const worksheetCancelKey = "dotty.worksheet.cancel"
2525

26+
/**
27+
* A wrapper around the information that VSCode needs to display text decorations.
28+
*
29+
* @param decorationType The styling options of this decoration
30+
* @param decorationOptions The options of this decoraiton.
31+
*/
32+
class Decoration {
33+
constructor(readonly decorationType: vscode.TextEditorDecorationType,
34+
readonly decorationOptions: vscode.DecorationOptions) {
35+
}
36+
}
37+
2638
/** A worksheet managed by vscode */
2739
class Worksheet implements Disposable {
2840

41+
/** The version of this document the last time it was run */
42+
private runVersion: number = -1
43+
2944
/** All decorations that have been added so far */
30-
private decorationTypes: vscode.TextEditorDecorationType[] = []
45+
private decorations: Decoration[] = []
3146

3247
/** The number of blank lines that have been inserted to fit the output so far. */
3348
private insertedLines: number = 0
@@ -62,9 +77,11 @@ class Worksheet implements Disposable {
6277

6378
/** Remove all decorations, and resets this worksheet. */
6479
private reset(): void {
65-
this.decorationTypes.forEach(decoration => decoration.dispose())
80+
this.decorations.forEach(decoration => decoration.decorationType.dispose())
81+
this.decorations = []
6682
this.insertedLines = 0
6783
this.decoratedLines.clear()
84+
this.runVersion = -1
6885
this.margin = this.longestLine() + 5
6986
}
7087

@@ -112,6 +129,7 @@ class Worksheet implements Disposable {
112129
const edit = new vscode.WorkspaceEdit()
113130
edit.set(this.document.uri, textEdits)
114131
vscode.workspace.applyEdit(edit).then(editSucceeded => {
132+
this.runVersion = this.document.version
115133
if (editSucceeded && !token.isCancellationRequested)
116134
resolve(vscode.window.withProgress({
117135
location: ProgressLocation.Window,
@@ -135,7 +153,8 @@ class Worksheet implements Disposable {
135153
}
136154

137155
/**
138-
* Parse and display the result of running part of this worksheet.
156+
* Parse and display the result of running part of this worksheet. The result is saved so that it
157+
* can be restored if this buffer is closed.
139158
*
140159
* @param lineNumber The number of the line in the source that produced the result.
141160
* @param runResult The result itself.
@@ -144,7 +163,7 @@ class Worksheet implements Disposable {
144163
* @return A `Thenable` that will insert necessary lines to fit the output
145164
* and display the decorations upon completion.
146165
*/
147-
public displayResult(lineNumber: number, runResult: string, editor: vscode.TextEditor) {
166+
public displayAndSaveResult(lineNumber: number, runResult: string, editor: vscode.TextEditor) {
148167
const resultLines = runResult.trim().split(/\r\n|\r|\n/g)
149168

150169
// The line where the next decoration should be put.
@@ -160,23 +179,41 @@ class Worksheet implements Disposable {
160179
const editPos = new vscode.Position(actualLine + 1, 0) // add after the line
161180
addNewLinesEdit.insert(editor.document.uri, editPos, "\n".repeat(linesToInsert))
162181
this.insertedLines += linesToInsert
182+
// Increase the `runVersion`, because the text edit will increase the document's version
183+
this.runVersion += 1
163184
}
164185

165186
return vscode.workspace.applyEdit(addNewLinesEdit).then(_ => {
166187
for (let line of resultLines) {
167188
const decorationPosition = new vscode.Position(actualLine, 0)
168189
const decorationMargin = this.margin - editor.document.lineAt(actualLine).text.length
169190
const decorationType = this.createDecoration(decorationMargin, line)
170-
this.decorationTypes.push(decorationType)
191+
const decorationOptions = { range: new vscode.Range(decorationPosition, decorationPosition), hoverMessage: line }
192+
const decoration = new Decoration(decorationType, decorationOptions)
193+
171194
this.decoratedLines.add(actualLine)
195+
this.decorations.push(decoration)
172196

173-
const decoration = { range: new vscode.Range(decorationPosition, decorationPosition), hoverMessage: line }
174-
editor.setDecorations(decorationType, [decoration])
197+
editor.setDecorations(decorationType, [decorationOptions])
175198
actualLine += 1
176199
}
177200
})
178201
}
179202

203+
/**
204+
* Restore the decorations that belong to this worksheet in `editor`. If the document has been
205+
* changed, since the last run of the worksheet, the decorations won't be added.
206+
*
207+
* @param editor The editor where to display the decorations.
208+
*/
209+
public restoreDecorations(editor: vscode.TextEditor) {
210+
if (editor.document.version == this.runVersion) {
211+
this.decorations.forEach(decoration => {
212+
editor.setDecorations(decoration.decorationType, [decoration.decorationOptions])
213+
})
214+
}
215+
}
216+
180217
/**
181218
* Create a new `TextEditorDecorationType` showing `text`. The decoration
182219
* will appear `margin` characters after the end of the line.
@@ -307,6 +344,14 @@ export class WorksheetProvider implements Disposable {
307344
this.worksheets.delete(document)
308345
}
309346
}),
347+
vscode.window.onDidChangeActiveTextEditor(editor => {
348+
if (editor) {
349+
const worksheet = this.worksheetFor(editor.document)
350+
if (worksheet) {
351+
worksheet.restoreDecorations(editor)
352+
}
353+
}
354+
}),
310355
vscode.commands.registerCommand(worksheetRunKey, () => {
311356
this.callOnActiveWorksheet(w => w.run())
312357
}),
@@ -375,7 +420,7 @@ export class WorksheetProvider implements Disposable {
375420
if (editor) {
376421
const worksheet = this.worksheetFor(editor.document)
377422
if (worksheet) {
378-
worksheet.displayResult(output.line - 1, output.content, editor)
423+
worksheet.displayAndSaveResult(output.line - 1, output.content, editor)
379424
}
380425
}
381426
}

0 commit comments

Comments
 (0)