@@ -14,145 +14,149 @@ import { client } from "../extension";
1414import _ from "lodash" ;
1515import { McqPanelWithLogging as McqPanel } from "../webview/components/McqPanel" ;
1616
17- let mcqPanel : vscode . WebviewPanel | null = null ;
17+ /*
18+ * MessageHandler is a singleton class that handles messages from the frontend
19+ * and manages the state of the webview panels.
20+ * Design choice: Active panel, mcqPanel, and activeEditor are all stored in the same class.
21+ * Trade-off: Requires an instance of MessageHandler to be created and used instead of exporting
22+ * the activeEditor and active panel directly.
23+ */
24+ export class MessageHandler {
25+ private messageQueue : MessageType [ ] = [ ] ;
26+ private handling = false ;
27+ public panel : vscode . WebviewPanel | null = null ;
28+ public mcqPanel : vscode . WebviewPanel | null = null ;
29+ public activeEditor : Editor | null = null ;
1830
19- let panel : vscode . WebviewPanel | null = null ;
20- // This needs to be a reference to active
21- // TODO: Fix this ugly code!
22- export let activeEditor : Editor | null = null ;
23-
24- const messageQueue : MessageType [ ] = [ ] ;
25- let handling = false ;
31+ async handleMessage ( context : vscode . ExtensionContext , message : MessageType ) {
32+ this . messageQueue . push ( message ) ;
33+ if ( this . handling ) {
34+ return ;
35+ }
36+ this . handling = true ;
2637
27- export async function handleMessage (
28- context : vscode . ExtensionContext ,
29- message : MessageType ,
30- ) {
31- messageQueue . push ( message ) ;
32- if ( handling ) {
33- return ;
34- }
35- handling = true ;
38+ while ( this . messageQueue . length > 0 ) {
39+ const message = this . messageQueue . shift ( ) ! ;
40+ console . log ( `${ Date . now ( ) } Beginning handleMessage: ${ message . type } ` ) ;
41+ switch ( message . type ) {
42+ case MessageTypeNames . ExtensionPing :
43+ sendToFrontend ( this . panel , Messages . ExtensionPong ( null ) ) ;
44+ break ;
45+ case MessageTypeNames . MCQQuestion : {
46+ if ( this . mcqPanel === null ) {
47+ this . mcqPanel = vscode . window . createWebviewPanel (
48+ "mcq-question-panel" ,
49+ `Question ${ message . questionId + 1 } ` ,
50+ vscode . ViewColumn . One ,
51+ { enableScripts : true , retainContextWhenHidden : true } ,
52+ ) ;
3653
37- while ( messageQueue . length > 0 ) {
38- const message = messageQueue . shift ( ) ! ;
39- console . log ( `${ Date . now ( ) } Beginning handleMessage: ${ message . type } ` ) ;
40- switch ( message . type ) {
41- case MessageTypeNames . ExtensionPing :
42- sendToFrontend ( panel , Messages . ExtensionPong ( null ) ) ;
43- break ;
44- case MessageTypeNames . MCQQuestion : {
45- if ( mcqPanel === null ) {
46- mcqPanel = vscode . window . createWebviewPanel (
47- "mcq-question-panel" ,
48- `Question ${ message . questionId + 1 } ` ,
49- vscode . ViewColumn . One ,
50- { enableScripts : true , retainContextWhenHidden : true } ,
54+ this . mcqPanel . onDidDispose ( ( ) => {
55+ this . mcqPanel = null ;
56+ } ) ;
57+ }
58+ this . mcqPanel . title = `Question ${ message . questionId + 1 } ` ;
59+ this . mcqPanel . iconPath = vscode . Uri . joinPath (
60+ context . extensionUri ,
61+ "assets" ,
62+ "icon.png" ,
5163 ) ;
52-
53- mcqPanel . onDidDispose ( ( ) => {
54- mcqPanel = null ;
64+ let activePanel = await McqPanel ( {
65+ data : {
66+ assessmentName : message . assessmentName ?? "" ,
67+ questionId : message . questionId ,
68+ question : message . question ,
69+ choices : message . options ,
70+ workspaceLocation : message . workspaceLocation ?? "assessment" ,
71+ } ,
5572 } ) ;
56- }
57- mcqPanel . title = `Question ${ message . questionId + 1 } ` ;
58- mcqPanel . iconPath = vscode . Uri . joinPath (
59- context . extensionUri ,
60- "assets" ,
61- "icon.png" ,
62- ) ;
63- let activePanel = await McqPanel ( {
64- data : {
65- assessmentName : message . assessmentName ?? "" ,
66- questionId : message . questionId ,
67- question : message . question ,
68- choices : message . options ,
69- workspaceLocation : message . workspaceLocation ?? "assessment" ,
70- } ,
71- } ) ;
72- setWebviewContent (
73- mcqPanel ,
74- context ,
75- < div
76- // @ts -ignore: Our FakeReact doesn't modify the style attribute
77- style = "width: 100%; height: calc(100vh - 10px)"
78- id = "mcq-panel"
79- >
80- { ReactDOMServer . renderToString ( activePanel ) }
81- </ div > ,
82- ) ;
83- mcqPanel . reveal ( vscode . ViewColumn . One ) ;
73+ setWebviewContent (
74+ this . mcqPanel ,
75+ context ,
76+ < div
77+ // @ts -ignore: Our FakeReact doesn't modify the style attribute
78+ style = "width: 100%; height: calc(100vh - 10px)"
79+ id = "mcq-panel"
80+ >
81+ { ReactDOMServer . renderToString ( activePanel ) }
82+ </ div > ,
83+ ) ;
84+ this . mcqPanel . reveal ( vscode . ViewColumn . One ) ;
8485
85- break ;
86- }
87- case MessageTypeNames . MCQAnswer :
88- console . log ( `Received MCQ answer: ${ message . choice } ` ) ;
89- sendToFrontend ( panel , message ) ;
90- break ;
91- case MessageTypeNames . NewEditor :
92- // console.log(message.questionType + " questionType \n");
93- // if (message.questionType == "mcq") {
94- // break;
95- // }
96- activeEditor = await Editor . create (
97- message . workspaceLocation ,
98- message . assessmentName ,
99- message . questionId ,
100- message . prepend ,
101- message . initialCode ,
102- ) ;
103- activeEditor . uri ;
104- const info = context . globalState . get ( "info" ) ?? { } ;
105- if ( activeEditor . uri ) {
106- // TODO: Write our own wrapper to set nested keys easily, removing lodash
107- // @ts -ignore
108- _ . set ( info , `["${ activeEditor . uri } "].chapter` , message . chapter ?? 1 ) ;
109- // TODO: message.prepend can be undefined in runtime, investigate
110- const nPrependLines =
111- message . prepend && message . prepend !== ""
112- ? message . prepend . split ( "\n" ) . length + 2 // account for start/end markers
113- : 0 ;
114- _ . set ( info , `["${ activeEditor . uri } "].prepend` , nPrependLines ) ;
115- context . globalState . update ( "info" , info ) ;
116- client . sendRequest ( "source/publishInfo" , info ) ;
86+ break ;
11787 }
118-
119- panel ?. reveal ( vscode . ViewColumn . Two ) ;
120- console . log (
121- `EXTENSION: NewEditor: activeEditor set to ${ activeEditor . assessmentName } _${ activeEditor . questionId } ` ,
122- ) ;
123-
124- activeEditor . onChange ( ( editor ) => {
125- const workspaceLocation = editor . workspaceLocation ;
126- const code = editor . getText ( ) ;
127- if ( ! code ) {
128- return ;
129- }
130- if ( editor !== activeEditor ) {
131- console . log (
132- `EXTENSION: Editor ${ editor . assessmentName } _${ editor . questionId } _${ editor . assessmentType } is no longer active, skipping onChange` ,
88+ case MessageTypeNames . MCQAnswer :
89+ console . log ( `Received MCQ answer: ${ message . choice } ` ) ;
90+ sendToFrontend ( this . panel , message ) ;
91+ break ;
92+ case MessageTypeNames . NewEditor :
93+ this . activeEditor = await Editor . create (
94+ message . workspaceLocation ,
95+ message . assessmentName ,
96+ message . questionId ,
97+ message . prepend ,
98+ message . initialCode ,
99+ ) ;
100+ this . activeEditor . uri ;
101+ const info = context . globalState . get ( "info" ) ?? { } ;
102+ if ( this . activeEditor . uri ) {
103+ // TODO: Write our own wrapper to set nested keys easily, removing lodash
104+ // @ts -ignore
105+ _ . set (
106+ info ,
107+ `["${ this . activeEditor . uri } "].chapter` ,
108+ message . chapter ?? 1 ,
133109 ) ;
110+ // TODO: message.prepend can be undefined in runtime, investigate
111+ const nPrependLines =
112+ message . prepend && message . prepend !== ""
113+ ? message . prepend . split ( "\n" ) . length + 2 // account for start/end markers
114+ : 0 ;
115+ _ . set ( info , `["${ this . activeEditor . uri } "].prepend` , nPrependLines ) ;
116+ context . globalState . update ( "info" , info ) ;
117+ client . sendRequest ( "source/publishInfo" , info ) ;
134118 }
135- if ( activeEditor ) {
136- console . log ( "activeEditor keys and values:" ) ;
137- Object . entries ( activeEditor ) . forEach ( ( [ key , value ] ) => {
138- console . log ( `${ key } :` , value ) ;
139- } ) ;
140- }
141- const message = Messages . Text ( workspaceLocation , code ) ;
142- console . log ( `Sending message: ${ JSON . stringify ( message ) } ` ) ;
143- sendToFrontend ( panel , message ) ;
144- } ) ;
145- break ;
146- // case MessageTypeNames.Text:
147- // if (!activeEditor) {
148- // console.log("ERROR: activeEditor is not set");
149- // break;
150- // }
151- // activeEditor.replace(message.code, "Text");
152- // break;
119+
120+ this . panel ?. reveal ( vscode . ViewColumn . Two ) ;
121+ console . log (
122+ `EXTENSION: NewEditor: activeEditor set to ${ this . activeEditor . assessmentName } _${ this . activeEditor . questionId } ` ,
123+ ) ;
124+
125+ this . activeEditor . onChange ( ( editor : Editor ) => {
126+ const workspaceLocation = editor . workspaceLocation ;
127+ const code = editor . getText ( ) ;
128+ if ( ! code ) {
129+ return ;
130+ }
131+ if ( editor !== this . activeEditor ) {
132+ console . log (
133+ `EXTENSION: Editor ${ editor . assessmentName } _${ editor . questionId } _${ editor . assessmentType } is no longer active, skipping onChange` ,
134+ ) ;
135+ }
136+ if ( this . activeEditor ) {
137+ console . log ( "activeEditor keys and values:" ) ;
138+ Object . entries ( this . activeEditor ) . forEach ( ( [ key , value ] ) => {
139+ console . log ( `${ key } :` , value ) ;
140+ } ) ;
141+ }
142+ const message = Messages . Text ( workspaceLocation , code ) ;
143+ console . log ( `Sending message: ${ JSON . stringify ( message ) } ` ) ;
144+ sendToFrontend ( this . panel , message ) ;
145+ } ) ;
146+ break ;
147+ }
148+ console . log ( `${ Date . now ( ) } Finish handleMessage: ${ message . type } ` ) ;
153149 }
154- console . log ( `${ Date . now ( ) } Finish handleMessage: ${ message . type } ` ) ;
150+
151+ this . handling = false ;
155152 }
156153
157- handling = false ;
154+ static instance : MessageHandler | null = null ;
155+
156+ static getInstance ( ) {
157+ if ( ! MessageHandler . instance ) {
158+ MessageHandler . instance = new MessageHandler ( ) ;
159+ }
160+ return MessageHandler . instance ;
161+ }
158162}
0 commit comments