1
1
'use strict'
2
2
3
3
import * as vscode from 'vscode'
4
+ import { LanguageClient } from 'vscode-languageclient'
4
5
5
6
/** All decorations that have been added so far */
6
7
let worksheetDecorationTypes : Map < vscode . TextDocument , vscode . TextEditorDecorationType [ ] > = new Map < vscode . TextDocument , vscode . TextEditorDecorationType [ ] > ( )
@@ -11,6 +12,75 @@ let worksheetInsertedLines: Map<vscode.TextDocument, number> = new Map<vscode.Te
11
12
/** The minimum margin to add so that the decoration is shown after all text. */
12
13
let worksheetMargin : Map < vscode . TextDocument , number > = new Map < vscode . TextDocument , number > ( )
13
14
15
+ /** Whether the given worksheet has finished evaluating. */
16
+ let worksheetFinished : Map < vscode . TextDocument , boolean > = new Map < vscode . TextDocument , boolean > ( )
17
+
18
+ /**
19
+ * The command key for evaluating a worksheet. Exposed to users as
20
+ * `Run worksheet`.
21
+ */
22
+ export const worksheetEvaluateKey = "worksheet.evaluate"
23
+
24
+ /**
25
+ * The command that is called to evaluate a worksheet after it has been evaluated.
26
+ *
27
+ * This is not exposed as a standalone callable command; but this command is triggered
28
+ * when a worksheet is saved.
29
+ */
30
+ export const worksheetEvaluateAfterSaveKey = "worksheet.evaluateAfterSave"
31
+
32
+ /** Is this document a worksheet? */
33
+ export function isWorksheet ( document : vscode . TextDocument ) : boolean {
34
+ return document . fileName . endsWith ( ".sc" )
35
+ }
36
+
37
+ /**
38
+ * This command is bound to `worksheetEvaluateAfterSaveKey`. This is implemented
39
+ * as a command, because we want to display a progress bar that may stay for a while, and
40
+ * VSCode will kill promises triggered by file save after some time.
41
+ */
42
+ export function evaluateCommand ( ) {
43
+ const editor = vscode . window . activeTextEditor
44
+ if ( editor ) {
45
+ const document = editor . document
46
+ if ( isWorksheet ( document ) ) {
47
+ showWorksheetProgress ( document )
48
+ }
49
+ }
50
+ }
51
+
52
+ /**
53
+ * The VSCode command executed when the user select `Run worksheet`.
54
+ *
55
+ * We check whether the buffer is dirty, and if it is, we save it. Evaluation will then be
56
+ * triggered by file save.
57
+ * If the buffer is clean, we do the necessary preparation for worksheet (compute margin,
58
+ * remove blank lines, etc.) and check if the buffer has been changed by that. If it is, we save
59
+ * and the evaluation will be triggered by file save.
60
+ * If the buffer is still clean, we send a `textDocument/didSave` notification to the language
61
+ * server in order to start the execution of the worksheet.
62
+ */
63
+ export function worksheetSave ( client : LanguageClient ) {
64
+ const editor = vscode . window . activeTextEditor
65
+ if ( editor ) {
66
+ const document = editor . document
67
+ if ( isWorksheet ( document ) ) {
68
+ if ( document . isDirty ) document . save ( )
69
+ else {
70
+ _prepareWorksheet ( document ) . then ( _ => {
71
+ if ( document . isDirty ) document . save ( )
72
+ else {
73
+ client . sendNotification ( "textDocument/didSave" , {
74
+ textDocument : { uri : document . uri . toString ( ) }
75
+ } )
76
+ showWorksheetProgress ( document )
77
+ }
78
+ } )
79
+ }
80
+ }
81
+ }
82
+ }
83
+
14
84
/**
15
85
* If the document that will be saved is a worksheet, resets the "worksheet state"
16
86
* (margin and number of inserted lines), and removes redundant blank lines that
@@ -22,18 +92,45 @@ let worksheetMargin: Map<vscode.TextDocument, number> = new Map<vscode.TextDocum
22
92
*/
23
93
export function prepareWorksheet ( event : vscode . TextDocumentWillSaveEvent ) {
24
94
const document = event . document
25
- if ( document . fileName . endsWith ( ".sc" ) ) {
26
- const setup =
27
- removeRedundantBlankLines ( document )
95
+ const setup = _prepareWorksheet ( document )
96
+ event . waitUntil ( setup )
97
+ }
98
+
99
+ function _prepareWorksheet ( document : vscode . TextDocument ) {
100
+ if ( isWorksheet ( document ) ) {
101
+ return removeRedundantBlankLines ( document )
28
102
. then ( _ => {
29
103
removeDecorations ( document )
30
104
worksheetMargin . set ( document , longestLine ( document ) + 5 )
31
105
worksheetInsertedLines . set ( document , 0 )
106
+ worksheetFinished . set ( document , false )
32
107
} )
33
- event . waitUntil ( setup )
108
+ } else {
109
+ return Promise . resolve ( )
34
110
}
35
111
}
36
112
113
+ function showWorksheetProgress ( document : vscode . TextDocument ) {
114
+ return vscode . window . withProgress ( {
115
+ location : vscode . ProgressLocation . Window ,
116
+ title : "Evaluating worksheet"
117
+ } , _ => {
118
+ function isFinished ( ) {
119
+ return worksheetFinished . get ( document ) || false
120
+ }
121
+ return wait ( isFinished , 500 )
122
+ } )
123
+ }
124
+
125
+ /** Wait until `cond` evaluates to true; test every `delay` ms. */
126
+ function wait ( cond : ( ) => boolean , delayMs : number ) : Promise < boolean > {
127
+ const isFinished = cond ( )
128
+ if ( isFinished ) {
129
+ return Promise . resolve ( true )
130
+ }
131
+ else return new Promise ( fn => setTimeout ( fn , delayMs ) ) . then ( _ => wait ( cond , delayMs ) )
132
+ }
133
+
37
134
/**
38
135
* Handle the result of evaluating part of a worksheet.
39
136
* This is called when we receive a `window/logMessage`.
@@ -49,7 +146,11 @@ export function worksheetHandleMessage(message: string) {
49
146
50
147
if ( editor ) {
51
148
let payload = message . slice ( editor . document . uri . toString ( ) . length )
52
- worksheetDisplayResult ( payload , editor )
149
+ if ( payload == "FINISHED" ) {
150
+ worksheetFinished . set ( editor . document , true )
151
+ } else {
152
+ worksheetDisplayResult ( payload , editor )
153
+ }
53
154
}
54
155
}
55
156
0 commit comments