22
33
44import * as Blockly from "blockly" ;
5- import { BlockCompilationResult , BlockCompileOptions , BlockDeclarationType , BlockDiagnostic , Environment , GrayBlockStatement , StdFunc , VarInfo , mkEnv } from "./environment" ;
5+ import { BlockCompilationResult , BlockCompileOptions , BlockDeclarationType , BlockDiagnostic , Environment , GrayBlockStatement , PxtBlock , StdFunc , VarInfo , mkEnv } from "./environment" ;
66import { IfBlock , attachPlaceholderIf , defaultValueForType , find , getConcreteType , getEscapedCBParameters , infer , isBooleanType , isFunctionRecursive , isStringType , lookup , returnType } from "./typeChecker" ;
77import { append , countOptionals , escapeVarName , forEachChildExpression , getInputTargetBlock , getLoopVariableField , isFunctionDefinition , isMutatingBlock , visibleParams } from "./util" ;
88import { isArrayType } from "../toolbox" ;
@@ -34,23 +34,28 @@ export const PXT_WARNING_ID = "WARNING_MESSAGE"
3434export function compileBlockAsync ( b : Blockly . Block , blockInfo : pxtc . BlocksInfo ) : Promise < BlockCompilationResult > {
3535 const w = b . workspace ;
3636 const e = mkEnv ( new SingleWorkspaceBlocksProgram ( w ) , blockInfo ) ;
37- infer ( w && w . getAllBlocks ( false ) , e ) ;
37+ const allBlocks = w ?. getAllBlocks ( false ) ;
38+ if ( allBlocks ) {
39+ for ( const block of allBlocks ) ( block as PxtBlock ) . PXT_FILE = "main.blocks" ;
40+ }
41+
42+ infer ( allBlocks , e ) ;
3843 const compiled = compileStatementBlock ( e , b )
3944 e . placeholders = { } ;
40- return tdASTtoTS ( e , compiled ) ;
45+ return tdASTtoTS ( e , { "main.blocks" : compiled } ) ;
4146}
4247
4348export function compileAsync ( b : Blockly . Workspace , blockInfo : pxtc . BlocksInfo , opts : BlockCompileOptions = { } ) : Promise < BlockCompilationResult > {
4449 const e = mkEnv ( new SingleWorkspaceBlocksProgram ( b ) , blockInfo , opts ) ;
4550 const [ nodes , diags ] = compileWorkspace ( e , b , blockInfo ) ;
46- const result = tdASTtoTS ( e , nodes , diags ) ;
51+ const result = tdASTtoTS ( e , { "main.blocks" : nodes } , diags ) ;
4752 return result ;
4853}
4954
5055export function compileProgramAsync ( program : BlocksProgram , blockInfo : pxtc . BlocksInfo , opts : BlockCompileOptions = { } ) : Promise < BlockCompilationResult > {
5156 const e = mkEnv ( program , blockInfo , opts ) ;
52- const [ nodes , diags ] = compileBlocksProgram ( e , program , blockInfo ) ;
53- return tdASTtoTS ( e , nodes , diags ) ;
57+ const [ files , diags ] = compileBlocksProgram ( e , program , blockInfo ) ;
58+ return tdASTtoTS ( e , files , diags ) ;
5459}
5560
5661function eventWeight ( b : Blockly . Block , e : Environment ) {
@@ -66,19 +71,31 @@ function eventWeight(b: Blockly.Block, e: Environment) {
6671 return - hash ;
6772}
6873
69- function compileBlocksProgram ( e : Environment , program : BlocksProgram , blockInfo : pxtc . BlocksInfo ) : [ pxt . blocks . JsNode [ ] , BlockDiagnostic [ ] ] {
74+ function compileBlocksProgram ( e : Environment , program : BlocksProgram , blockInfo : pxtc . BlocksInfo ) : [ pxt . Map < pxt . blocks . JsNode [ ] > , BlockDiagnostic [ ] ] {
7075 try {
7176 // all compiled top level blocks are events
7277 program . refreshSymbols ( ) ;
7378
7479 let allBlocks : Blockly . Block [ ] = [ ] ;
7580 let topblocks : Blockly . Block [ ] = [ ] ;
76- let topComments : Blockly . comments . WorkspaceComment [ ] = [ ] ;
81+
82+ const blocksByFile : pxt . Map < Blockly . Block [ ] > = { } ;
83+ const stmtsByFile : pxt . Map < pxt . blocks . JsNode [ ] > = { } ;
7784
7885 for ( const workspace of program . getAllWorkspaces ( ) ) {
79- allBlocks . push ( ...workspace . getAllBlocks ( false ) ) ;
80- topblocks . push ( ...workspace . getTopBlocks ( true ) ) ;
81- topComments . push ( ...workspace . getTopComments ( true ) ) ;
86+ const ws = workspace . workspace ;
87+ const fileTopBlocks = ws . getTopBlocks ( true ) ;
88+ const fileAllBlocks = ws . getAllBlocks ( false ) ;
89+
90+ for ( const block of fileAllBlocks ) {
91+ ( block as PxtBlock ) . PXT_FILE = workspace . fileName ;
92+ }
93+
94+ allBlocks . push ( ...fileAllBlocks ) ;
95+ topblocks . push ( ...fileTopBlocks ) ;
96+
97+ blocksByFile [ workspace . fileName ] = fileTopBlocks ;
98+ stmtsByFile [ workspace . fileName ] = [ ] ;
8299 }
83100
84101 if ( pxt . react . getTilemapProject ) {
@@ -94,32 +111,44 @@ function compileBlocksProgram(e: Environment, program: BlocksProgram, blockInfo:
94111 // drop disabled blocks
95112 allBlocks = allBlocks . filter ( b => b . isEnabled ( ) ) ;
96113 topblocks = topblocks . filter ( b => b . isEnabled ( ) ) ;
97- trackAllVariables ( topblocks , e ) ;
114+ trackAllVariables ( blocksByFile , e ) ;
98115 infer ( allBlocks , e ) ;
99116
100- const stmtsMain : pxt . blocks . JsNode [ ] = [ ] ;
101117
102118 // compile workspace comments, add them to the top
103- const commentMap = groupWorkspaceComments ( topblocks as Blockly . BlockSvg [ ] ,
104- topComments as Blockly . comments . RenderedWorkspaceComment [ ] ) ;
119+ let metaMap : CommentMap = {
120+ idToComments : { } ,
121+ orphans : [ ]
122+ }
105123
106- commentMap . orphans . forEach ( comment => append ( stmtsMain , compileWorkspaceComment ( comment ) . children ) ) ;
124+ for ( const workspace of program . getAllWorkspaces ( ) ) {
125+ const topBlocks = blocksByFile [ workspace . fileName ] ;
126+ const comments = workspace . workspace . getTopComments ( true ) ;
107127
108- topblocks . forEach ( b => {
109- if ( commentMap . idToComments [ b . id ] ) {
110- commentMap . idToComments [ b . id ] . forEach ( comment => {
111- append ( stmtsMain , compileWorkspaceComment ( comment ) . children ) ;
128+ const commentMap = groupWorkspaceComments ( topBlocks as Blockly . BlockSvg [ ] , comments as Blockly . comments . RenderedWorkspaceComment [ ] ) ;
129+ commentMap . orphans . forEach ( comment => append ( stmtsByFile [ workspace . fileName ] , compileWorkspaceComment ( comment ) . children ) ) ;
130+
131+ for ( const id of Object . keys ( commentMap . idToComments ) ) {
132+ metaMap . idToComments [ id ] = commentMap . idToComments [ id ] ;
133+ }
134+ }
135+
136+ for ( const b of topblocks ) {
137+ const stmts = stmtsByFile [ ( b as PxtBlock ) . PXT_FILE ] ;
138+ if ( metaMap . idToComments [ b . id ] ) {
139+ metaMap . idToComments [ b . id ] . forEach ( comment => {
140+ append ( stmts , compileWorkspaceComment ( comment ) . children ) ;
112141 } ) ;
113142 }
114143 if ( b . type == ts . pxtc . ON_START_TYPE )
115- append ( stmtsMain , compileStatementBlock ( e , b ) ) ;
144+ append ( stmts , compileStatementBlock ( e , b ) ) ;
116145 else {
117146 const compiled = pxt . blocks . mkBlock ( compileStatementBlock ( e , b ) ) ;
118147 if ( compiled . type == pxt . blocks . NT . Block )
119- append ( stmtsMain , compiled . children ) ;
120- else stmtsMain . push ( compiled )
148+ append ( stmts , compiled . children ) ;
149+ else stmts . push ( compiled )
121150 }
122- } ) ;
151+ }
123152
124153 const stmtsEnums : pxt . blocks . JsNode [ ] = [ ] ;
125154 e . enums . forEach ( info => {
@@ -196,11 +225,14 @@ function compileBlocksProgram(e: Environment, program: BlocksProgram, blockInfo:
196225
197226 e . diagnostics . push ( {
198227 blockId : v . firstReference && v . firstReference . id ,
199- message : lf ( "Variable '{0}' is never assigned" , v . name )
228+ message : lf ( "Variable '{0}' is never assigned" , v . name ) ,
229+ fileName : v . fileName
200230 } ) ;
201231 } ) ;
202232
203- return [ stmtsEnums . concat ( leftoverVars . concat ( stmtsMain ) ) , e . diagnostics ] ;
233+ stmtsByFile [ "main.blocks" ] = stmtsEnums . concat ( leftoverVars . concat ( stmtsByFile [ "main.blocks" ] ) ) ;
234+
235+ return [ stmtsByFile , e . diagnostics ] ;
204236
205237 } catch ( err ) {
206238 let be : Blockly . Block = ( err as any ) . block ;
@@ -223,6 +255,10 @@ function compileWorkspace(e: Environment, w: Blockly.Workspace, blockInfo: pxtc.
223255 // all compiled top level blocks are events
224256 let allBlocks = w . getAllBlocks ( false ) ;
225257
258+ for ( const block of allBlocks ) {
259+ ( block as PxtBlock ) . PXT_FILE = "main.blocks" ;
260+ }
261+
226262 if ( pxt . react . getTilemapProject ) {
227263 pxt . react . getTilemapProject ( ) . removeInactiveBlockAssets ( allBlocks . map ( b => b . id ) ) ;
228264 }
@@ -238,7 +274,7 @@ function compileWorkspace(e: Environment, w: Blockly.Workspace, blockInfo: pxtc.
238274 // drop disabled blocks
239275 allBlocks = allBlocks . filter ( b => b . isEnabled ( ) ) ;
240276 topblocks = topblocks . filter ( b => b . isEnabled ( ) ) ;
241- trackAllVariables ( topblocks , e ) ;
277+ trackAllVariables ( { "main.blocks" : topblocks } , e ) ;
242278 infer ( allBlocks , e ) ;
243279
244280 const stmtsMain : pxt . blocks . JsNode [ ] = [ ] ;
@@ -340,7 +376,8 @@ function compileWorkspace(e: Environment, w: Blockly.Workspace, blockInfo: pxtc.
340376
341377 e . diagnostics . push ( {
342378 blockId : v . firstReference && v . firstReference . id ,
343- message : lf ( "Variable '{0}' is never assigned" , v . name )
379+ message : lf ( "Variable '{0}' is never assigned" , v . name ) ,
380+ fileName : v . fileName
344381 } ) ;
345382 } ) ;
346383
@@ -972,15 +1009,26 @@ function compileImage(e: Environment, b: Blockly.Block, frames: number, columns:
9721009 return pxt . blocks . H . namespaceCall ( n , f , [ lit ] . concat ( args ) , false ) ;
9731010}
9741011
975- function tdASTtoTS ( env : Environment , app : pxt . blocks . JsNode [ ] , diags ?: BlockDiagnostic [ ] ) : Promise < BlockCompilationResult > {
976- let res = pxt . blocks . flattenNode ( app )
1012+ function tdASTtoTS ( env : Environment , app : pxt . Map < pxt . blocks . JsNode [ ] > , diags ?: BlockDiagnostic [ ] ) : Promise < BlockCompilationResult > {
1013+ const outfiles : pxt . Map < string > = { } ;
9771014
978- // Note: the result of format is not used!
1015+ let concatenated : string = "" ;
1016+ const sourceMap : pxt . blocks . BlockSourceInterval [ ] = [ ] ;
9791017
980- return workerOpAsync ( "format" , { format : { input : res . output , pos : 1 } } ) . then ( ( ) => {
1018+ const getOutfile = ( file : string ) => file . replace ( / \. b l o c k s $ / , ".ts" ) ;
1019+
1020+ for ( const file of Object . keys ( app ) ) {
1021+ const res = pxt . blocks . flattenNode ( app [ file ] ) ;
1022+ outfiles [ getOutfile ( file ) ] = res . output ;
1023+ sourceMap . push ( ...res . sourceMap ) ;
1024+ concatenated += res . output + "\n" ;
1025+ }
1026+
1027+ // Note: the result of format is not used!
1028+ return workerOpAsync ( "format" , { format : { input : concatenated , pos : 1 } } ) . then ( ( ) => {
9811029 return {
982- source : res . output ,
983- sourceMap : res . sourceMap ,
1030+ outfiles ,
1031+ sourceMap,
9841032 stats : env . stats ,
9851033 diagnostics : diags || [ ]
9861034 } ;
@@ -1329,13 +1377,15 @@ function compileReturnStatement(e: Environment, b: Blockly.Block, comments: stri
13291377 if ( ! parentFunction ) {
13301378 e . diagnostics . push ( {
13311379 blockId : b . id ,
1332- message : lf ( "Return statements can only be used within function bodies." )
1380+ message : lf ( "Return statements can only be used within function bodies." ) ,
1381+ fileName : ( b as PxtBlock ) . PXT_FILE
13331382 } ) ;
13341383 }
13351384 else if ( hasReturn && parentFunction . type !== FUNCTION_DEFINITION_BLOCK_TYPE ) {
13361385 e . diagnostics . push ( {
13371386 blockId : b . id ,
1338- message : lf ( "Return statements can only return values inside function definitions." )
1387+ message : lf ( "Return statements can only return values inside function definitions." ) ,
1388+ fileName : ( b as PxtBlock ) . PXT_FILE
13391389 } ) ;
13401390 }
13411391
0 commit comments