@@ -8,38 +8,46 @@ import { sendToFrontendWrapped } from "../commands/showPanel";
88import { canonicaliseLocation } from "./misc" ;
99import { codeAddPrepend , codeRemovePrepend } from "./editorUtils" ;
1010
11+ /**
12+ * Represents a VS Code editor associated with a Source Academy question.
13+ * Abstracts low level calling of VS Code APIs.
14+ */
1115export class Editor {
12- editor ?: vscode . TextEditor ;
16+ private editor : vscode . TextEditor ;
17+ private onChangeCallback ?: ( editor : Editor ) => void ;
18+
19+ // Data associated with TextEditor
20+ prepend : string ;
21+ uri : string ;
22+
23+ // Metadata relating to this question
1324 workspaceLocation : VscWorkspaceLocation ;
1425 assessmentName : string ;
1526 questionId : number ;
16- assessmentType : string | null = null ;
17- onChangeCallback ?: ( editor : Editor ) => void ;
18- code : string | null = null ;
19- uri : string | null = null ;
2027
21- // For debugging purposes
22- replaceTime : number = 0 ;
23- nBursty : number = 0 ;
24-
25- constructor (
28+ private constructor (
29+ editor : vscode . TextEditor ,
30+ prepend : string ,
31+ uri : string ,
2632 workspaceLocation : VscWorkspaceLocation ,
2733 assessmentName : string ,
2834 questionId : number ,
2935 ) {
36+ this . editor = editor ;
37+ this . prepend = prepend ;
38+ this . uri = uri ;
3039 this . workspaceLocation = workspaceLocation ;
3140 this . assessmentName = assessmentName ;
32- this . assessmentType = this . assessmentType ;
3341 this . questionId = questionId ;
3442 }
3543
3644 /** For debugging purposes */
37- log ( text : string ) {
38- console . log ( `${ this . editor ? .document . fileName . split ( "/" ) . at ( - 1 ) } ${ text } ` ) ;
45+ private log ( text : string ) {
46+ console . log ( `${ this . editor . document . fileName . split ( "/" ) . at ( - 1 ) } ${ text } ` ) ;
3947 }
4048
4149 getText ( ) {
42- return this . editor ? .document . getText ( ) ;
50+ return this . editor . document . getText ( ) ;
4351 }
4452
4553 // TODO: This method is too loaded, it's not obvious it also shows the editor
@@ -50,19 +58,12 @@ export class Editor {
5058 prepend : string = "" ,
5159 initialCode : string = "" ,
5260 ) : Promise < Editor > {
53- const self = new Editor ( workspaceLocation , assessmentName , questionId ) ;
54- self . assessmentName = assessmentName ;
55- self . questionId = questionId ;
56-
5761 const workspaceFolder = canonicaliseLocation ( config . workspaceFolder ) ;
58-
5962 const filePath = path . join (
6063 workspaceFolder ,
6164 `${ assessmentName } _${ questionId } .js` ,
6265 ) ;
63-
6466 const uri = vscode . Uri . file ( filePath ) ;
65- self . uri = uri . toString ( ) ;
6667
6768 const contents = codeAddPrepend ( initialCode , prepend ) ;
6869
@@ -72,9 +73,6 @@ export class Editor {
7273 . then (
7374 ( localCode ) => {
7475 if ( localCode !== contents ) {
75- self . log (
76- "EXTENSION: Conflict detected between local and remote, prompting user to choose one" ,
77- ) ;
7876 vscode . window
7977 . showInformationMessage (
8078 [
@@ -87,15 +85,14 @@ export class Editor {
8785 . then ( async ( answer ) => {
8886 // By default the code displayed is the local one
8987 if ( answer === "Yes" ) {
90- self . log ( "EXTENSION: Saving program from server to file" ) ;
9188 await vscode . workspace . fs . writeFile (
9289 uri ,
9390 new TextEncoder ( ) . encode ( contents ) ,
9491 ) ;
9592 } else if ( answer === undefined ) {
9693 // Modal cancelled
9794 const message = Messages . Text (
98- self . workspaceLocation ,
95+ workspaceLocation ,
9996 codeRemovePrepend ( localCode ) ,
10097 ) ;
10198 sendToFrontendWrapped ( message ) ;
@@ -104,7 +101,6 @@ export class Editor {
104101 }
105102 } ,
106103 async ( ) => {
107- self . log ( `Opening file failed, creating at ${ filePath } ` ) ;
108104 await vscode . workspace . fs . writeFile (
109105 uri ,
110106 new TextEncoder ( ) . encode ( contents ) ,
@@ -116,79 +112,58 @@ export class Editor {
116112 preview : false ,
117113 viewColumn : vscode . ViewColumn . One ,
118114 } ) ;
115+
116+ // Programmatically set the language
119117 vscode . languages . setTextDocumentLanguage ( editor . document , "source" ) ;
118+
119+ // Collapse the prepend section
120120 editor . selection = new vscode . Selection (
121121 editor . document . positionAt ( 0 ) ,
122122 editor . document . positionAt ( 1 ) ,
123123 ) ;
124124 vscode . commands . executeCommand ( "editor.fold" ) ;
125125
126- self . editor = editor ;
126+ // Create wrapper
127+ const self = new Editor (
128+ editor ,
129+ prepend ,
130+ uri . toString ( ) ,
131+ workspaceLocation ,
132+ assessmentName ,
133+ questionId ,
134+ ) ;
135+
136+ // Register callback when contents changed
127137 vscode . workspace . onDidChangeTextDocument (
128138 ( e : vscode . TextDocumentChangeEvent ) => {
129139 if ( ! self . onChangeCallback ) {
130140 return ;
131141 }
132- const text = editor . document . getText ( ) ;
133142 if ( e . contentChanges . length === 0 ) {
134143 self . log ( `EXTENSION: Editor's code did not change, ignoring` ) ;
135144 return ;
136145 }
137- if ( Date . now ( ) - self . replaceTime < 1000 ) {
138- self . log (
139- `EXTENSION: Ignoring change event, ${ Date . now ( ) - self . replaceTime } ms since last replace` ,
140- ) ;
141- return ;
142- }
143- self . log ( `EXTENSION: Editor's code changed!` ) ;
144146 self . onChangeCallback ( self ) ;
145- self . code = text ;
146147 } ,
147148 ) ;
149+
148150 return self ;
149151 }
150152
151- async replace ( code : string , tag : string = "" ) {
152- if ( ! this . editor ) {
153- return ;
154- }
155- this . log ( `EXTENSION: Editor's replace called by ${ tag } : <<${ code } >>` ) ;
156- if ( this . nBursty > 5 ) {
157- if ( Date . now ( ) - this . replaceTime < 5000 ) {
158- this . log ( `EXTENSION: TOO BURSTY` ) ;
159- return ;
160- }
161- this . nBursty = 0 ;
162- }
163- if ( Date . now ( ) - this . replaceTime < 1000 ) {
164- this . nBursty ++ ;
165- }
166- // this.disableCallback = true;
153+ async replace ( code : string ) {
167154 const editor = this . editor ;
168- // Don't replace if the code is the same
155+ const contents = codeAddPrepend ( code , this . prepend ) ;
156+
157+ // In some sense, simulate a select all and paste
169158 editor . edit ( ( editBuilder ) => {
170159 editBuilder . replace (
171160 new vscode . Range (
172161 editor . document . positionAt ( 0 ) ,
173162 editor . document . positionAt ( editor . document . getText ( ) . length ) ,
174163 ) ,
175- code ,
164+ contents ,
176165 ) ;
177166 } ) ;
178- let retry = 0 ;
179- while ( editor . document . getText ( ) !== code ) {
180- await new Promise ( ( r ) => setTimeout ( r , 100 ) ) ;
181- this . log (
182- `EXTENSION: Editor's not replace yet, lets wait: ${ editor . document . getText ( ) } ` ,
183- ) ;
184- retry ++ ;
185- if ( retry > 11 ) {
186- this . log ( `EXTENSION: Editor's replace wait limit reached` ) ;
187- break ;
188- }
189- }
190- this . code = code ;
191- this . replaceTime = Date . now ( ) ;
192167 }
193168
194169 onChange (
0 commit comments