Browse Source

Squashed 'src/ted2go/' changes from de1d6d4a..5de45d56

5de45d56 Fixed "local var is visible outside of its scope".
3247b1f4 F1 and F2 now works with ?. operator.
c78e8723 Added "Use opengl 'es' mode" option as a preference (active by default).
ced9bbdc Added "Build & Run" action into Project view.
6913d451 Added opening of project by drag-n-drop its .mx2proj file.
893f5a14 Improved project system.
f34962df Project system.
7e8f1c2c Fixes in project system.
ba7cf43e Added project file support (.mx2proj)
1218a946 Removed broken code assigned to F10 key.
16517d02 Now files outside of active project are parsed w/o locking them
c6be140d Continue working on new parsing system
5c462148 New parsing stuff to have local members in completion!
a2a0cfa3 Local members in autocompletion - first try!
6d76a937 Merge branch 'master' into dev

git-subtree-dir: src/ted2go
git-subtree-split: 5de45d5603c0508a13dd88ef15b4e57f611859e3
Mark Sibly 7 years ago
parent
commit
9f62f60793

+ 2 - 1
.gitignore

@@ -3,4 +3,5 @@
 *.buildv*
 *.buildv*
 *.products/
 *.products/
 *.app
 *.app
-*.DS_*
+*.DS_*
+*.mx2/

+ 82 - 62
MainWindow.monkey2

@@ -30,16 +30,16 @@ Class MainWindowInstance Extends Window
 		
 		
 		_tabsWrap=New DraggableTabs
 		_tabsWrap=New DraggableTabs
 		
 		
-		_docsTabView=New TabViewExt( TabViewFlags.DraggableTabs|TabViewFlags.ClosableTabs )
+		_docsTabView=Di.Resolve<DocsTabView>()
 		
 		
 		_recentFilesMenu=New MenuExt( "Recent files" )
 		_recentFilesMenu=New MenuExt( "Recent files" )
 		_recentProjectsMenu=New MenuExt( "Recent projects" )
 		_recentProjectsMenu=New MenuExt( "Recent projects" )
 		_closeProjectMenu=New MenuExt( "Close project" )
 		_closeProjectMenu=New MenuExt( "Close project" )
 		
 		
-		_docBrowser=New DockingView
+		_docBrowser=Di.Resolve<DocBrowserView>()
+		
+		_docsManager=Di.Resolve<DocumentManager>()
 		
 		
-		_docsManager=New DocumentManager( _docsTabView,_docBrowser )
-
 		_docsManager.CurrentDocumentChanged+=Lambda()
 		_docsManager.CurrentDocumentChanged+=Lambda()
 			
 			
 			UpdateKeyView()
 			UpdateKeyView()
@@ -59,7 +59,7 @@ Class MainWindowInstance Extends Window
 		
 		
 		_docsManager.DocumentDoubleClicked+=Lambda( doc:Ted2Document )
 		_docsManager.DocumentDoubleClicked+=Lambda( doc:Ted2Document )
 		
 		
-			_buildActions.LockBuildFile()
+			_docsManager.LockBuildFile()
 		End
 		End
 		
 		
 		App.FileDropped+=Lambda( path:String )
 		App.FileDropped+=Lambda( path:String )
@@ -68,24 +68,32 @@ Class MainWindowInstance Extends Window
 		End
 		End
 		
 		
 		_docsManager.DocumentAdded+=Lambda( doc:Ted2Document )
 		_docsManager.DocumentAdded+=Lambda( doc:Ted2Document )
+			
 			AddRecentFile( doc.Path )
 			AddRecentFile( doc.Path )
 			
 			
 			Local codeDoc:=Cast<CodeDocument>( doc )
 			Local codeDoc:=Cast<CodeDocument>( doc )
-			If codeDoc Then codeDoc.AnalyzeIndentation()
+			If codeDoc
+				codeDoc.AnalyzeIndentation()
+				If _projectView.ActiveProject?.MainFilePath=codeDoc.Path
+					_docsManager.SetAsMainFile( codeDoc.Path,True )
+				Endif
+			Endif
 			
 			
 			SaveState()
 			SaveState()
 		End
 		End
 		
 		
 		_docsManager.DocumentRemoved+=Lambda( doc:Ted2Document )
 		_docsManager.DocumentRemoved+=Lambda( doc:Ted2Document )
+			
 			If IsTmpPath( doc.Path ) DeleteFile( doc.Path )
 			If IsTmpPath( doc.Path ) DeleteFile( doc.Path )
 			SaveState()
 			SaveState()
 		End
 		End
 		
 		
 		_recentViewedFiles=New RecentFiles( _docsManager )
 		_recentViewedFiles=New RecentFiles( _docsManager )
 		
 		
+		
 		'Build tab
 		'Build tab
 		
 		
-		_buildConsole=New ConsoleExt
+		_buildConsole=Di.Resolve<BuildConsole>()
 		' jump to errors etc.
 		' jump to errors etc.
 		_buildConsole.RequestJumpToFile+=Lambda( path:String,line:Int )
 		_buildConsole.RequestJumpToFile+=Lambda( path:String,line:Int )
 			
 			
@@ -96,7 +104,7 @@ Class MainWindowInstance Extends Window
 		
 		
 		'Output tab
 		'Output tab
 		
 		
-		_outputConsole=New ConsoleExt
+		_outputConsole=Di.Resolve<OutputConsole>()
 		Local bar:=New ToolBarExt
 		Local bar:=New ToolBarExt
 		bar.MaxSize=New Vec2i( 300,30 )
 		bar.MaxSize=New Vec2i( 300,30 )
 		
 		
@@ -151,7 +159,7 @@ Class MainWindowInstance Extends Window
 		
 		
 		'Help tab
 		'Help tab
 		
 		
-		_helpView=New HtmlViewExt
+		_helpView=Di.Resolve<HelpView>()
 		_docsConsole=New DockingView
 		_docsConsole=New DockingView
 		bar=New ToolBarExt
 		bar=New ToolBarExt
 		bar.MaxSize=New Vec2i( 300,30 )
 		bar.MaxSize=New Vec2i( 300,30 )
@@ -185,7 +193,7 @@ Class MainWindowInstance Extends Window
 			label.Text=url
 			label.Text=url
 		End
 		End
 		
 		
-		_helpTree=New HelpTreeView( _helpView )
+		_helpTree=Di.Resolve<HelpTreeView>()
 		_docsConsole.AddView( _helpTree,"right",200,True )
 		_docsConsole.AddView( _helpTree,"right",200,True )
 		
 		
 		_docsConsole.AddView( bar,"top" )
 		_docsConsole.AddView( bar,"top" )
@@ -206,9 +214,9 @@ Class MainWindowInstance Extends Window
 		_helpView.Navigate( AboutPagePath )
 		_helpView.Navigate( AboutPagePath )
 		
 		
 		
 		
-		_debugView=New DebugView( _docsManager,_outputConsole )
+		_debugView=Di.Resolve<DebugView>()
 		
 		
-		_buildActions=New BuildActions( _docsManager,_buildConsole,_debugView )
+		_buildActions=Di.Resolve<BuildActions>()
 		_buildActions.ErrorsOccured+=Lambda( errors:BuildError[] )
 		_buildActions.ErrorsOccured+=Lambda( errors:BuildError[] )
 			
 			
 			ShowBuildConsole( True )
 			ShowBuildConsole( True )
@@ -217,10 +225,10 @@ Class MainWindowInstance Extends Window
 		
 		
 		' ProjectView
 		' ProjectView
 		'
 		'
-		_projectView=New ProjectView( _docsManager,_buildActions )
+		_projectView=Di.Resolve<ProjectView>()
 		' project opened
 		' project opened
-		_projectView.ProjectOpened+=Lambda( dir:String )
-			AddRecentProject( dir )
+		_projectView.ProjectOpened+=Lambda( path:String )
+			AddRecentProject( path )
 			SaveState()
 			SaveState()
 		End
 		End
 		' project closed
 		' project closed
@@ -230,6 +238,14 @@ Class MainWindowInstance Extends Window
 			_findActions.FindInFiles( folder )
 			_findActions.FindInFiles( folder )
 		End
 		End
 		
 		
+		_codeParsing=New CodeParsing( _docsManager,_projectView )
+		
+		_projectView.MainFileChanged+=Lambda( path:String,prevPath:String )
+		
+			_docsManager.SetAsMainFile( prevPath,False )
+			_docsManager.SetAsMainFile( path,True )
+		End
+		
 		_fileActions=New FileActions( _docsManager )
 		_fileActions=New FileActions( _docsManager )
 		_editActions=New EditActions( _docsManager )
 		_editActions=New EditActions( _docsManager )
 		_findActions=New FindActions( _docsManager,_projectView,_findConsole )
 		_findActions=New FindActions( _docsManager,_projectView,_findConsole )
@@ -247,7 +263,7 @@ Class MainWindowInstance Extends Window
 		_tabMenu.AddAction( _fileActions.saveAs )
 		_tabMenu.AddAction( _fileActions.saveAs )
 		_tabMenu.AddSeparator()
 		_tabMenu.AddSeparator()
 		_tabMenu.AddAction( _buildActions.lockBuildFile )
 		_tabMenu.AddAction( _buildActions.lockBuildFile )
-		
+		_tabMenu.AddAction( _projectView.setMainFile )
 		_tabMenu.AddSeparator()
 		_tabMenu.AddSeparator()
 		_tabMenu.AddAction( "Open on Desktop" ).Triggered=Lambda()
 		_tabMenu.AddAction( "Open on Desktop" ).Triggered=Lambda()
 			
 			
@@ -309,7 +325,8 @@ Class MainWindowInstance Extends Window
 		_fileMenu.AddAction( _fileActions.saveAs )
 		_fileMenu.AddAction( _fileActions.saveAs )
 		_fileMenu.AddAction( _fileActions.saveAll )
 		_fileMenu.AddAction( _fileActions.saveAll )
 		_fileMenu.AddSeparator()
 		_fileMenu.AddSeparator()
-		_fileMenu.AddAction( _projectView.openProject )
+		_fileMenu.AddAction( _projectView.openProjectFolder )
+		_fileMenu.AddAction( _projectView.openProjectFile )
 		_fileMenu.AddSubMenu( _recentProjectsMenu )
 		_fileMenu.AddSubMenu( _recentProjectsMenu )
 		_fileMenu.AddSubMenu( _closeProjectMenu )
 		_fileMenu.AddSubMenu( _closeProjectMenu )
 		_fileMenu.AddSeparator()
 		_fileMenu.AddSeparator()
@@ -534,6 +551,18 @@ Class MainWindowInstance Extends Window
 		'SDL_RaiseWindow( SDLWindow )
 		'SDL_RaiseWindow( SDLWindow )
 	End
 	End
 	
 	
+	Method OnFileDropped( path:String )
+	
+		New Fiber( Lambda()
+	
+			Local ok:=_projectView.OnFileDropped( path )
+			If Not ok And FileExists( path ) 'file
+				_docsManager.OpenDocument( path,True )
+			Endif
+	
+		End )
+	End
+	
 	Method HideFindPanel:Bool()
 	Method HideFindPanel:Bool()
 		
 		
 		If Not _findReplaceView.Visible Return False
 		If Not _findReplaceView.Visible Return False
@@ -635,9 +664,14 @@ Class MainWindowInstance Extends Window
 		Return _docsManager
 		Return _docsManager
 	End
 	End
 	
 	
+	Property ProjView:ProjectView()
+	
+		Return _projectView
+	End
+	
 	Property LockedDocument:CodeDocument()
 	Property LockedDocument:CodeDocument()
 	
 	
-		Return _buildActions.LockedDocument
+		Return _docsManager.LockedDocument
 	End
 	End
 	
 	
 	Property IsTerminating:Bool()
 	Property IsTerminating:Bool()
@@ -949,7 +983,7 @@ Class MainWindowInstance Extends Window
 		_toolBar.AddSeparator()
 		_toolBar.AddSeparator()
 		_toolBar.AddIconicButton( ThemeImages.Get( "toolbar/new_file.png" ),_fileActions.new_.Triggered,newTitle )
 		_toolBar.AddIconicButton( ThemeImages.Get( "toolbar/new_file.png" ),_fileActions.new_.Triggered,newTitle )
 		_toolBar.AddIconicButton( ThemeImages.Get( "toolbar/open_file.png" ),_fileActions.open.Triggered,openTitle )
 		_toolBar.AddIconicButton( ThemeImages.Get( "toolbar/open_file.png" ),_fileActions.open.Triggered,openTitle )
-		_toolBar.AddIconicButton( ThemeImages.Get( "toolbar/open_project.png" ),_projectView.openProject.Triggered,"Open project..." )
+		_toolBar.AddIconicButton( ThemeImages.Get( "toolbar/open_project.png" ),_projectView.openProjectFolder.Triggered,"Open project..." )
 		Local icons:=New Image[]( ThemeImages.Get( "toolbar/save.png" ),ThemeImages.Get( "toolbar/save_dirty.png" ) )
 		Local icons:=New Image[]( ThemeImages.Get( "toolbar/save.png" ),ThemeImages.Get( "toolbar/save_dirty.png" ) )
 		_saveItem=_toolBar.AddIconicButton( icons,_fileActions.save.Triggered,saveTitle )
 		_saveItem=_toolBar.AddIconicButton( icons,_fileActions.save.Triggered,saveTitle )
 		icons=New Image[]( ThemeImages.Get( "toolbar/save_all.png" ),ThemeImages.Get( "toolbar/save_all_dirty.png" ) )
 		icons=New Image[]( ThemeImages.Get( "toolbar/save_all.png" ),ThemeImages.Get( "toolbar/save_all_dirty.png" ) )
@@ -1060,23 +1094,23 @@ Class MainWindowInstance Extends Window
 			Return
 			Return
 		Endif
 		Endif
 		
 		
-		Local doc:=Cast<CodeDocumentView>( _docsManager.CurrentTextView )
-		If Not doc Return
+		Local view:=Cast<CodeDocumentView>( _docsManager.CurrentTextView )
+		If Not view Return
 		
 		
 		' don't check with parser, just find in docs tree
 		' don't check with parser, just find in docs tree
 		If searchMode
 		If searchMode
-			Local ident:=doc.WordAtCursor
+			Local ident:=view.WordAtCursor
 			_helpTree.QuickHelp( ident )
 			_helpTree.QuickHelp( ident )
 			ShowDocsView( True )
 			ShowDocsView( True )
 			Return
 			Return
 		Endif
 		Endif
 		
 		
-		Local ident:=doc.FullIdentAtCursor
+		Local ident:=view.IdentBeforeCursor()
 		
 		
 		If Not ident Return
 		If Not ident Return
 		
 		
-		Local parser:=ParsersManager.Get( doc.FileType )
-		Local item:=parser.ItemAtScope( ident,doc.FilePath,doc.LineNumAtCursor )
+		Local parser:=ParsersManager.Get( view.FileType )
+		Local item:=parser.ItemAtScope( ident,view.FilePath,GetCursorPos( view ) )
 		
 		
 		If item
 		If item
 			
 			
@@ -1126,7 +1160,7 @@ Class MainWindowInstance Extends Window
 			
 			
 			__prevPath=path
 			__prevPath=path
 			
 			
-		ElseIf KeywordsManager.Get( doc.FileType ).Contains( ident )
+		Elseif KeywordsManager.Get( view.FileType ).Contains( ident )
 			
 			
 			ShowStatusBarText( "(keyword) "+ident )
 			ShowStatusBarText( "(keyword) "+ident )
 		
 		
@@ -1232,7 +1266,7 @@ Class MainWindowInstance Extends Window
 	
 	
 	Method GotoDeclaration()
 	Method GotoDeclaration()
 	
 	
-		Local doc:=Cast<CodeDocument>( _docsManager.CurrentDocument )
+		Local doc:=_docsManager.CurrentCodeDocument
 		If Not doc Return
 		If Not doc Return
 		
 		
 		doc.GotoDeclaration()
 		doc.GotoDeclaration()
@@ -1307,9 +1341,9 @@ Class MainWindowInstance Extends Window
 	End
 	End
 
 
 	Method OpenDocument( path:String,lockIt:Bool=False )
 	Method OpenDocument( path:String,lockIt:Bool=False )
-	
+		
 		_docsManager.OpenDocument( path,True )
 		_docsManager.OpenDocument( path,True )
-		If lockIt Then _buildActions.LockBuildFile()
+		If lockIt Then _docsManager.LockBuildFile()
 		UpdateWindow( True )
 		UpdateWindow( True )
 	End
 	End
 	
 	
@@ -1361,18 +1395,6 @@ Class MainWindowInstance Extends Window
 		_docsTabView.EnsureVisibleCurrentTab()
 		_docsTabView.EnsureVisibleCurrentTab()
 	End
 	End
 	
 	
-	Method OnFileDropped( path:String )
-		
-		New Fiber( Lambda()
-			
-			Local ok:=_projectView.OnFileDropped( path )
-			If Not ok And FileExists( path ) 'file
-				_docsManager.OpenDocument( path,True )
-			Endif
-			
-		End )
-	End
-	
 	Method OnAppClose()
 	Method OnAppClose()
 		
 		
 		_fileActions.quit.Trigger()
 		_fileActions.quit.Trigger()
@@ -1393,7 +1415,7 @@ Class MainWindowInstance Extends Window
 	
 	
 	Method OnProjectClosed( dir:String )
 	Method OnProjectClosed( dir:String )
 		
 		
-		UpdateCloseProjectMenu( dir )
+		UpdateCloseProjectMenu()
 		
 		
 		Local list:=New Stack<Ted2Document>
 		Local list:=New Stack<Ted2Document>
 		' close all related files
 		' close all related files
@@ -1626,11 +1648,11 @@ Class MainWindowInstance Extends Window
 		
 		
 		App.Idle+=Lambda() 'delay execution
 		App.Idle+=Lambda() 'delay execution
 			
 			
-			_docsManager.LoadState( jobj )
 			_buildActions.LoadState( jobj )
 			_buildActions.LoadState( jobj )
 			_projectView.LoadState( jobj )
 			_projectView.LoadState( jobj )
-		 
-			If Not _projectView.OpenProjects _projectView.OpenProject( CurrentDir() )
+			_docsManager.LoadState( jobj )
+		 	
+			If Not _projectView.HasOpenedProjects Then _projectView.OpenProject( CurrentDir() )
 			
 			
 			UpdateRecentFilesMenu()
 			UpdateRecentFilesMenu()
 			UpdateRecentProjectsMenu()
 			UpdateRecentProjectsMenu()
@@ -1653,7 +1675,7 @@ Class MainWindowInstance Extends Window
 	Protected
 	Protected
 	
 	
 	Method OnKeyEvent( event:KeyEvent ) Override
 	Method OnKeyEvent( event:KeyEvent ) Override
-	
+		
 		Select event.Type
 		Select event.Type
 		Case EventType.KeyDown
 		Case EventType.KeyDown
 			
 			
@@ -1697,7 +1719,7 @@ Class MainWindowInstance Extends Window
 	End
 	End
 	
 	
 	Method OnWindowEvent( event:WindowEvent ) Override
 	Method OnWindowEvent( event:WindowEvent ) Override
-
+		
 		Select event.Type
 		Select event.Type
 			
 			
 			Case EventType.WindowClose
 			Case EventType.WindowClose
@@ -1793,6 +1815,7 @@ Class MainWindowInstance Extends Window
 	Field _storedSize:Recti,_storedMaximized:Bool
 	Field _storedSize:Recti,_storedMaximized:Bool
 	
 	
 	Field _recentViewedFiles:RecentFiles
 	Field _recentViewedFiles:RecentFiles
+	Field _codeParsing:CodeParsing
 	
 	
 	Method ToJson:JsonValue( rect:Recti )
 	Method ToJson:JsonValue( rect:Recti )
 		Return New JsonArray( New JsonValue[]( New JsonNumber( rect.min.x ),New JsonNumber( rect.min.y ),New JsonNumber( rect.max.x ),New JsonNumber( rect.max.y ) ) )
 		Return New JsonArray( New JsonValue[]( New JsonNumber( rect.min.x ),New JsonNumber( rect.min.y ),New JsonNumber( rect.max.x ),New JsonNumber( rect.max.y ) ) )
@@ -1821,7 +1844,7 @@ Class MainWindowInstance Extends Window
 		If _recentProjects.Length>10 Then _recentProjects.Resize( 10 )
 		If _recentProjects.Length>10 Then _recentProjects.Resize( 10 )
 		
 		
 		UpdateRecentProjectsMenu()
 		UpdateRecentProjectsMenu()
-		UpdateCloseProjectMenu( path )
+		UpdateCloseProjectMenu()
 	End
 	End
 	
 	
 	Method UpdateRecentFilesMenu()
 	Method UpdateRecentFilesMenu()
@@ -1844,34 +1867,31 @@ Class MainWindowInstance Extends Window
 	End
 	End
 	
 	
 	Method UpdateRecentProjectsMenu()
 	Method UpdateRecentProjectsMenu()
-	
+		
 		_recentProjectsMenu.Clear()
 		_recentProjectsMenu.Clear()
-	
+		
 		Local recents:=New StringStack
 		Local recents:=New StringStack
-	
+		
 		For Local path:=Eachin _recentProjects
 		For Local path:=Eachin _recentProjects
-			If GetFileType( path )<>FileType.Directory Continue
-	
 			_recentProjectsMenu.AddAction( path ).Triggered=Lambda()
 			_recentProjectsMenu.AddAction( path ).Triggered=Lambda()
 				_projectView.OpenProject( path )
 				_projectView.OpenProject( path )
 			End
 			End
-	
 			recents.Add( path )
 			recents.Add( path )
 		Next
 		Next
-	
+		
 		_recentProjects=recents
 		_recentProjects=recents
 	End
 	End
 	
 	
-	Method UpdateCloseProjectMenu( dir:String="" )
-	
-		_closeProjectMenu.Clear()
+	Method UpdateCloseProjectMenu()
 		
 		
-		For Local dir:=Eachin _projectView.OpenProjects
+		_closeProjectMenu.Clear()
 		
 		
-			_closeProjectMenu.AddAction( dir ).Triggered=Lambda()
+		For Local proj:=Eachin _projectView.OpenProjects
 			
 			
-				_projectView.CloseProject( dir )
-				
+			Local path:=proj.Path
+			_closeProjectMenu.AddAction( path ).Triggered=Lambda()
+			
+				_projectView.CloseProject( path )
 				UpdateCloseProjectMenu()
 				UpdateCloseProjectMenu()
 			End
 			End
 			
 			

+ 46 - 0
PathsProvider.monkey2

@@ -0,0 +1,46 @@
+
+Namespace ted2go
+
+
+Class PathsProvider
+	
+	Function GetActiveMainFilePath:String( checkCurrentDoc:Bool=True,showNoMainFileAlert:Bool=False )
+		
+		If _customBuildProject
+			If showNoMainFileAlert Then ProjectView.CheckMainFilePath( _customBuildProject )
+			Return _customBuildProject.MainFilePath
+		Endif
+		
+		Local path:=MainWindow.DocsManager.LockedDocument?.Path
+		If Not path
+			Local proj:=GetActiveProject()
+			If proj
+				If showNoMainFileAlert Then ProjectView.CheckMainFilePath( proj )
+				Return proj.MainFilePath
+			Endif
+		Endif
+		If Not path And checkCurrentDoc Then path=MainWindow.DocsManager.CurrentDocument?.Path
+		
+'		Print "locked: "+_docsManager.LockedDocument?.Path
+'		Print "active: "+_projectView.ActiveProject?.MainFilePath
+'		Print "current: "+_docsManager.CurrentDocument?.Path
+		
+		Return path
+	End
+	
+	Function SetCustomBuildProject( proj:Monkey2Project )
+	
+		_customBuildProject=proj
+	End
+	
+	Private
+	
+	Global _customBuildProject:Monkey2Project
+	
+	Function GetActiveProject:Monkey2Project()
+	
+		If _customBuildProject Return _customBuildProject
+	
+		Return MainWindow.ProjView.ActiveProject
+	End
+End

+ 1 - 1
Plugin.monkey2

@@ -5,7 +5,7 @@ Namespace ted2go
 Class Plugin
 Class Plugin
 
 
 	Property Name:String() Virtual
 	Property Name:String() Virtual
-	
+		
 		Return "<untitled plugin>"
 		Return "<untitled plugin>"
 	End
 	End
 	
 	

+ 4 - 1
Prefs.monkey2

@@ -43,7 +43,8 @@ Class PrefsInstance
 	'
 	'
 	Field MonkeyRootPath:String
 	Field MonkeyRootPath:String
 	Field IdeHomeDir:String
 	Field IdeHomeDir:String
-	'	
+	Field OpenGlProfile:="es"
+	'
 	Field SiblyMode:Bool
 	Field SiblyMode:Bool
 	
 	
 	Property FindFilesFilter:String()
 	Property FindFilesFilter:String()
@@ -63,6 +64,7 @@ Class PrefsInstance
 			MainProjectIcons=Json_GetBool( j2,"projectIcons",MainProjectIcons )
 			MainProjectIcons=Json_GetBool( j2,"projectIcons",MainProjectIcons )
       		MainProjectSingleClickExpanding=Json_GetBool( j2,"singleClickExpanding",MainProjectSingleClickExpanding )
       		MainProjectSingleClickExpanding=Json_GetBool( j2,"singleClickExpanding",MainProjectSingleClickExpanding )
       		MainPlaceDocsAtBegin=Json_GetBool( j2,"placeDocsAtBegin",MainPlaceDocsAtBegin )
       		MainPlaceDocsAtBegin=Json_GetBool( j2,"placeDocsAtBegin",MainPlaceDocsAtBegin )
+      		OpenGlProfile=Json_GetString( j2,"openglProfile",OpenGlProfile )
       		
       		
 		Endif
 		Endif
 		
 		
@@ -125,6 +127,7 @@ Class PrefsInstance
 		j["projectIcons"]=New JsonBool( MainProjectIcons )
 		j["projectIcons"]=New JsonBool( MainProjectIcons )
 		j["singleClickExpanding"]=New JsonBool( MainProjectSingleClickExpanding )
 		j["singleClickExpanding"]=New JsonBool( MainProjectSingleClickExpanding )
 		j["placeDocsAtBegin"]=New JsonBool( MainPlaceDocsAtBegin )
 		j["placeDocsAtBegin"]=New JsonBool( MainPlaceDocsAtBegin )
+		j["openglProfile"]=New JsonBool( OpenGlProfile )
 		
 		
 		j=New JsonObject
 		j=New JsonObject
 		json["completion"]=j
 		json["completion"]=j

+ 33 - 26
Ted2.monkey2

@@ -63,6 +63,7 @@
 #Import "parser/Parser"
 #Import "parser/Parser"
 #Import "parser/Monkey2Parser"
 #Import "parser/Monkey2Parser"
 #Import "parser/ParserPlugin"
 #Import "parser/ParserPlugin"
+#Import "parser/CodeParsing"
 
 
 #Import "product/BuildProduct"
 #Import "product/BuildProduct"
 #Import "product/Mx2ccEnv"
 #Import "product/Mx2ccEnv"
@@ -120,7 +121,7 @@
 #Import "view/Undock"
 #Import "view/Undock"
 #Import "view/TextViewExt"
 #Import "view/TextViewExt"
 
 
-
+#Import "PathsProvider"
 #Import "Tree"
 #Import "Tree"
 #Import "Tuple"
 #Import "Tuple"
 #Import "Plugin"
 #Import "Plugin"
@@ -130,6 +131,9 @@
 #Import "LiveTemplates"
 #Import "LiveTemplates"
 #Import "MainWindow"
 #Import "MainWindow"
 
 
+#Import "di/Di"
+#Import "di/DiSetup"
+
 
 
 Namespace ted2go
 Namespace ted2go
 
 
@@ -139,13 +143,16 @@ Using mojox..
 Using tinyxml2..
 Using tinyxml2..
 Using sdl2..
 Using sdl2..
 
 
+
 Const MONKEY2_DOMAIN:="http://monkeycoder.co.nz"
 Const MONKEY2_DOMAIN:="http://monkeycoder.co.nz"
 
 
-Global AppTitle:="Ted2Go v2.10"
+Global AppTitle:="Ted2Go v2.11"
 
 
 
 
 Function Main()
 Function Main()
-
+	
+	SetupDiContainer()
+	
 	Prefs.LoadLocalState()
 	Prefs.LoadLocalState()
 	
 	
 	Local root:=Prefs.MonkeyRootPath
 	Local root:=Prefs.MonkeyRootPath
@@ -165,19 +172,23 @@ Function Main()
 	'
 	'
 	Local jobj:=JsonObject.Load( "bin/ted2.state.json" )
 	Local jobj:=JsonObject.Load( "bin/ted2.state.json" )
 	If Not jobj jobj=New JsonObject
 	If Not jobj jobj=New JsonObject
-
+	
 	Prefs.LoadState( jobj )
 	Prefs.LoadState( jobj )
 	
 	
 	'initial theme
 	'initial theme
 	'
 	'
 	If Not jobj.Contains( "theme" ) jobj["theme"]=New JsonString( "theme-hollow" )
 	If Not jobj.Contains( "theme" ) jobj["theme"]=New JsonString( "theme-hollow" )
-
+	
 	If Not jobj.Contains( "themeScale" ) jobj["themeScale"]=New JsonNumber( 1 )
 	If Not jobj.Contains( "themeScale" ) jobj["themeScale"]=New JsonNumber( 1 )
 	
 	
 	SetConfig( "MOJO_INITIAL_THEME",jobj.GetString( "theme" ) )
 	SetConfig( "MOJO_INITIAL_THEME",jobj.GetString( "theme" ) )
 	
 	
 	SetConfig( "MOJO_INITIAL_THEME_SCALE",jobj.GetString( "themeScale" ) )
 	SetConfig( "MOJO_INITIAL_THEME_SCALE",jobj.GetString( "themeScale" ) )
 	
 	
+	If Prefs.OpenGlProfile
+		SetConfig( "MOJO_OPENGL_PROFILE",Prefs.OpenGlProfile )
+	Endif
+	
 	'start the app!
 	'start the app!
 	'
 	'
 	New AppInstance
 	New AppInstance
@@ -185,7 +196,7 @@ Function Main()
 	'initial window state
 	'initial window state
 	'
 	'
 	Local flags:=WindowFlags.Resizable|WindowFlags.HighDPI
 	Local flags:=WindowFlags.Resizable|WindowFlags.HighDPI
-
+	
 	Local rect:Recti
 	Local rect:Recti
 	
 	
 	If jobj.Contains( "windowRect" ) 
 	If jobj.Contains( "windowRect" ) 
@@ -196,7 +207,7 @@ Function Main()
 		rect=New Recti( 0,0,w,h )
 		rect=New Recti( 0,0,w,h )
 		flags|=WindowFlags.Center
 		flags|=WindowFlags.Center
 	Endif
 	Endif
-
+	
 	New MainWindowInstance( AppTitle,rect,flags,jobj )
 	New MainWindowInstance( AppTitle,rect,flags,jobj )
 	
 	
 	App.Idle+=Lambda()
 	App.Idle+=Lambda()
@@ -206,11 +217,7 @@ Function Main()
 		For Local i:=1 Until args.Length
 		For Local i:=1 Until args.Length
 			Local arg:=args[i]
 			Local arg:=args[i]
 			arg=arg.Replace( "\","/" )
 			arg=arg.Replace( "\","/" )
-			If GetFileType( arg ) = FileType.File
-				MainWindow.OpenDocument( arg,True )
-			Elseif GetFileType( arg ) = FileType.Directory
-				MainWindow.OpenProject( arg )
-			Endif
+			MainWindow.OnFileDropped( arg )
 		Next
 		Next
 	End
 	End
 	
 	
@@ -221,7 +228,7 @@ End
 Function SetupMonkeyRootPath:String( rootPath:String,searchMode:Bool )
 Function SetupMonkeyRootPath:String( rootPath:String,searchMode:Bool )
 	
 	
 #If __DESKTOP_TARGET__
 #If __DESKTOP_TARGET__
-
+	
 	If searchMode
 	If searchMode
 		' search for desired folder
 		' search for desired folder
 		Local found:=FindBinFolder( rootPath )
 		Local found:=FindBinFolder( rootPath )
@@ -229,7 +236,7 @@ Function SetupMonkeyRootPath:String( rootPath:String,searchMode:Bool )
 		If Not found And rootPath<>AppDir() Then found=FindBinFolder( AppDir() )
 		If Not found And rootPath<>AppDir() Then found=FindBinFolder( AppDir() )
 		' search for choosen-by-requester folder
 		' search for choosen-by-requester folder
 		While Not found
 		While Not found
-	
+			
 			Local ok:=Confirm( "Initializing","Monkey2 root directory isn't set.~nTo continue, you should specify it." )
 			Local ok:=Confirm( "Initializing","Monkey2 root directory isn't set.~nTo continue, you should specify it." )
 			If Not ok
 			If Not ok
 				Return ""
 				Return ""
@@ -255,30 +262,30 @@ Function SetupMonkeyRootPath:String( rootPath:String,searchMode:Bool )
 End
 End
 
 
 Function GetActionTextWithShortcut:String( action:Action )
 Function GetActionTextWithShortcut:String( action:Action )
-
+	
 	Return action.Text+" ("+action.HotKeyText+")"
 	Return action.Text+" ("+action.HotKeyText+")"
 End
 End
 
 
 Function Exec( exePath:String,args:String="" )
 Function Exec( exePath:String,args:String="" )
-
+	
 #If __HOSTOS__="windows"
 #If __HOSTOS__="windows"
-
+	
 	libc.system( exePath+" "+args )
 	libc.system( exePath+" "+args )
 	
 	
 #Else If __HOSTOS__="macos"
 #Else If __HOSTOS__="macos"
-
+	
 	libc.system( "open ~q"+exePath+"~q --args "+args )
 	libc.system( "open ~q"+exePath+"~q --args "+args )
-
+	
 #Else If __HOSTOS__="linux"
 #Else If __HOSTOS__="linux"
-
+	
 	libc.system( exePath+" "+args+" >/dev/null 2>/dev/null &" )
 	libc.system( exePath+" "+args+" >/dev/null 2>/dev/null &" )
-
+	
 #Else If __HOSTOS__="raspbian"
 #Else If __HOSTOS__="raspbian"
-
+	
 	libc.system( exePath+" "+args+" >/dev/null 2>/dev/null &" )
 	libc.system( exePath+" "+args+" >/dev/null 2>/dev/null &" )
-
+	
 #Endif
 #Endif
-
+	
 End
 End
 
 
 
 
@@ -291,13 +298,13 @@ Function FindBinFolder:String( startingFolder:String )
 	ChangeDir( startingFolder )
 	ChangeDir( startingFolder )
 	
 	
 	While GetFileType( "bin" )<>FileType.Directory Or GetFileType( "modules" )<>FileType.Directory
 	While GetFileType( "bin" )<>FileType.Directory Or GetFileType( "modules" )<>FileType.Directory
-	
+		
 		If IsRootDir( CurrentDir() )
 		If IsRootDir( CurrentDir() )
 			
 			
 			ok=False
 			ok=False
 			Exit
 			Exit
 		Endif
 		Endif
-	
+		
 		ChangeDir( ExtractDir( CurrentDir() ) )
 		ChangeDir( ExtractDir( CurrentDir() ) )
 	Wend
 	Wend
 	Local result:=ok ? CurrentDir() Else ""
 	Local result:=ok ? CurrentDir() Else ""

+ 4 - 0
Ted2Go.mx2proj

@@ -0,0 +1,4 @@
+{
+	"hidden":[".git*","bin","*.buildv*","*.products","logo",".mx2"],
+	"mainFile":"Ted2.monkey2"
+}

+ 21 - 69
action/BuildActions.monkey2

@@ -62,11 +62,6 @@ Class BuildActions Implements IModuleBuilder
 		_console=console
 		_console=console
 		_debugView=debugView
 		_debugView=debugView
 		
 		
-		_docs.DocumentRemoved+=Lambda( doc:Ted2Document )
-
-			If doc=_locked _locked=Null
-		End
-		
 		buildAndRun=New Action( "Run" )
 		buildAndRun=New Action( "Run" )
 #If __TARGET__="macos"
 #If __TARGET__="macos"
 		buildAndRun.HotKey=Key.R
 		buildAndRun.HotKey=Key.R
@@ -111,7 +106,7 @@ Class BuildActions Implements IModuleBuilder
 		nextError.HotKey=Key.F4
 		nextError.HotKey=Key.F4
 		
 		
 		lockBuildFile=New Action( "Lock build file" )
 		lockBuildFile=New Action( "Lock build file" )
-		lockBuildFile.Triggered=LockBuildFile
+		lockBuildFile.Triggered=_docs.LockBuildFile
 		lockBuildFile.HotKey=Key.L
 		lockBuildFile.HotKey=Key.L
 		lockBuildFile.HotKeyModifiers=Modifier.Menu
 		lockBuildFile.HotKeyModifiers=Modifier.Menu
 		
 		
@@ -208,41 +203,22 @@ Class BuildActions Implements IModuleBuilder
 		Endif
 		Endif
 	End
 	End
 	
 	
-	Property LockedDocument:CodeDocument()
-	
-		Return _locked
-	End
-	
 	Property Verbosed:Bool()
 	Property Verbosed:Bool()
 	
 	
 		Return _verboseMode.Checked
 		Return _verboseMode.Checked
 	End
 	End
 	
 	
-	Method LockBuildFile()
-		
-		Local doc:=Cast<CodeDocument>( _docs.CurrentDocument )
-		OnLockBuildFile( doc )
-	End
-	
 	Method SaveState( jobj:JsonObject )
 	Method SaveState( jobj:JsonObject )
 		
 		
-		If _locked jobj["lockedDocument"]=New JsonString( _locked.Path )
-		
 		jobj["buildConfig"]=New JsonString( _buildConfig )
 		jobj["buildConfig"]=New JsonString( _buildConfig )
 		
 		
 		jobj["buildTarget"]=New JsonString( _buildTarget )
 		jobj["buildTarget"]=New JsonString( _buildTarget )
 		
 		
 		jobj["buildVerbose"]=New JsonBool( _verboseMode.Checked )
 		jobj["buildVerbose"]=New JsonBool( _verboseMode.Checked )
 	End
 	End
-		
+	
 	Method LoadState( jobj:JsonObject )
 	Method LoadState( jobj:JsonObject )
 	
 	
-		If jobj.Contains( "lockedDocument" )
-			Local path:=jobj["lockedDocument"].ToString()
-			_locked=Cast<CodeDocument>( _docs.FindDocument( path ) )
-			If _locked Then SetLockedState( _locked,True )
-		Endif
-		
 		If jobj.Contains( "buildConfig" )
 		If jobj.Contains( "buildConfig" )
 			_buildConfig=jobj["buildConfig"].ToString()
 			_buildConfig=jobj["buildConfig"].ToString()
 			Select _buildConfig
 			Select _buildConfig
@@ -290,7 +266,7 @@ Class BuildActions Implements IModuleBuilder
 		Wend
 		Wend
 	
 	
 		Local idle:=Not _console.Running
 		Local idle:=Not _console.Running
-		Local canbuild:=idle And BuildDoc()<>Null And _buildTarget
+		Local canbuild:=idle And FilePathToBuild And _buildTarget
 		
 		
 		build.Enabled=canbuild
 		build.Enabled=canbuild
 		buildAndRun.Enabled=canbuild
 		buildAndRun.Enabled=canbuild
@@ -378,8 +354,6 @@ Class BuildActions Implements IModuleBuilder
 	Field _console:ConsoleExt
 	Field _console:ConsoleExt
 	Field _debugView:DebugView
 	Field _debugView:DebugView
 	
 	
-	Field _locked:CodeDocument
-	
 	Field _errors:=New List<BuildError>
 	Field _errors:=New List<BuildError>
 	
 	
 	Field _buildConfig:String
 	Field _buildConfig:String
@@ -401,11 +375,14 @@ Class BuildActions Implements IModuleBuilder
 	Field _storedTargets:String
 	Field _storedTargets:String
 	Field _storedClean:Bool
 	Field _storedClean:Bool
 	
 	
-	Method BuildDoc:CodeDocument()
-		
-		If Not _locked Return Cast<CodeDocument>( _docs.CurrentDocument )
-		
-		Return _locked
+	Property FilePathToBuild:String()
+	
+		Return PathsProvider.GetActiveMainFilePath()
+	End
+	
+	Property FilePathToBuildWithPrompt:String()
+	
+		Return PathsProvider.GetActiveMainFilePath( True,True )
 	End
 	End
 	
 	
 	Method SaveAll:Bool( buildFile:String )
 	Method SaveAll:Bool( buildFile:String )
@@ -564,10 +541,10 @@ Class BuildActions Implements IModuleBuilder
 	
 	
 	Method BuildApp:Bool( config:String,target:String,sourceAction:String )
 	Method BuildApp:Bool( config:String,target:String,sourceAction:String )
 	
 	
-		Local buildDoc:=BuildDoc()
-		If Not buildDoc Return False
+		Local buildDocPath:=FilePathToBuildWithPrompt
+		If Not buildDocPath Return False
 		
 		
-		Local product:=BuildProduct.GetBuildProduct( buildDoc.Path,target,False )
+		Local product:=BuildProduct.GetBuildProduct( buildDocPath,target,False )
 		If Not product Return False
 		If Not product Return False
 		
 		
 		Local opts:=product.GetMx2ccOpts()
 		Local opts:=product.GetMx2ccOpts()
@@ -581,12 +558,12 @@ Class BuildActions Implements IModuleBuilder
 		If Verbosed cmd+=" -verbose"
 		If Verbosed cmd+=" -verbose"
 		cmd+=" -config="+config
 		cmd+=" -config="+config
 		cmd+=" -target="+target
 		cmd+=" -target="+target
-		cmd+=" ~q"+buildDoc.Path+"~q"
+		cmd+=" ~q"+buildDocPath+"~q"
 		
 		
 		Local title := sourceAction="build" ? "Building" Else (sourceAction="run" ? "Running" Else "Checking")
 		Local title := sourceAction="build" ? "Building" Else (sourceAction="run" ? "Running" Else "Checking")
-		Local msg:=title+" ~ "+target+" ~ "+config+" ~ "+StripDir( buildDoc.Path )
+		Local msg:=title+" ~ "+target+" ~ "+config+" ~ "+StripDir( buildDocPath )
 		
 		
-		If Not BuildMx2( cmd,msg,sourceAction,buildDoc.Path,True ) Return False
+		If Not BuildMx2( cmd,msg,sourceAction,buildDocPath,True ) Return False
 		
 		
 		_console.Write("~nDone.")
 		_console.Write("~nDone.")
 		
 		
@@ -662,41 +639,16 @@ Class BuildActions Implements IModuleBuilder
 		If _errors.Empty Return
 		If _errors.Empty Return
 		
 		
 		_errors.AddLast( _errors.RemoveFirst() )
 		_errors.AddLast( _errors.RemoveFirst() )
-			
-		GotoError( _errors.First )
-	End
-	
-	Method OnLockBuildFile( doc:CodeDocument )
-	
-		If Not doc Return
 		
 		
-		If _locked Then SetLockedState( _locked,False )
-		
-		If doc=_locked
-			_locked=Null
-			Return
-		Endif
-		
-		_locked=doc
-		SetLockedState( _locked,True )
-		
-		
-	End
-	
-	Method SetLockedState( doc:CodeDocument,locked:Bool )
-		
-		doc.State=locked ? "+" Else ""
-		Local tab:=_docs.FindTab( doc.View )
-		If tab Then tab.SetLockedState( locked )
-		_docs.CurrentDocumentChanged()
+		GotoError( _errors.First )
 	End
 	End
 	
 	
 	Method OnBuildFileSettings()
 	Method OnBuildFileSettings()
 
 
-		Local buildDoc:=BuildDoc()
-		If Not buildDoc Return
+		Local path:=FilePathToBuild
+		If Not path Return
 		
 		
-		local product:=BuildProduct.GetBuildProduct( buildDoc.Path,_buildTarget,True )
+		Local product:=BuildProduct.GetBuildProduct( path,_buildTarget,True )
 	End
 	End
 	
 	
 	Method OnUpdateModules()
 	Method OnUpdateModules()

+ 1 - 1
action/FindActions.monkey2

@@ -56,7 +56,7 @@ Class FindActions
 			Local path:=docs.CurrentDocument?.Path
 			Local path:=docs.CurrentDocument?.Path
 			If Not path Then path=projView.SelectedItem?.Path
 			If Not path Then path=projView.SelectedItem?.Path
 			
 			
-			Local proj:=projView.FindProjectByFile( path )
+			Local proj:=ProjectView.FindProject( path )?.Folder
 			OnFindInFiles( "",proj )
 			OnFindInFiles( "",proj )
 			
 			
 		End
 		End

BIN
assets/themes/project/monkey2main.png


+ 78 - 0
di/Di.monkey2

@@ -0,0 +1,78 @@
+
+Namespace ted2go
+
+
+#Rem monkeydoc Container class that stores bindings of type <-> realization_of_type.
+Type can be interface or class type.
+#End
+Class Di Final
+	
+	#Rem monkeydoc This function add new binding.
+	
+	You can add the only type of T function.
+	
+	@param bindFunc a function that provide realization of T type
+	@param singleton if true - we want to have the only instance of T (singleton), if false - we want to instantiate new T every call of Resolve()
+	#End
+	Function Bind<T>( bindFunc:T(),singleton:Bool=True )
+		
+		Local type:=Typeof<T>
+		If singleton
+			Assert( Not BINDERS_SINGLE.Contains( type ),"Di-container : type "+type+" already exists!" )
+			BINDERS_SINGLE[type]=bindFunc
+		Else
+			Assert( Not BINDERS_NEW_INST.Contains( type ),"Di-container : type "+type+" already exists!" )
+			BINDERS_NEW_INST[type]=bindFunc
+		Endif
+		
+	End
+	
+	#Rem monkeydoc Binding with parameterless constructor of T.
+	#End
+	Function Bind<T>( singleton:Bool=True )
+		
+		Bind( Lambda:T()
+			Return New T
+		End, 
+		singleton )
+	End
+	
+	#Rem monkeydoc This function return instance of T.
+	
+	If type was added as singleton - we always will get the same instance.
+	Otherwise - we always will get new instance.
+	#End
+	Function Resolve<T>:T()
+		
+		Local type:=Typeof<T>
+		Local binder:=BINDERS_NEW_INST[type]
+		If binder<>Null
+			Return Cast<T()>( binder )()
+		Endif
+		
+		Local result:=SINGLETONS[type]
+		If result=Null
+			
+			binder=BINDERS_SINGLE[type]
+			If binder
+				Local result2:=Cast<T()>( binder )()
+				Assert( result2<>Null,"Di-container: realization for "+type+" not found!" )
+				SINGLETONS[type]=result2
+				
+				Return result2
+			Endif
+			
+		Endif
+		
+		Assert( result<>Null,"Di-container: realization for "+type+" not found!" )
+		
+		Return Cast<T>( result )
+	End
+	
+	Private
+	
+	Const SINGLETONS:=New Map<TypeInfo,Variant>
+	Const BINDERS_NEW_INST:=New Map<TypeInfo,Variant>
+	Const BINDERS_SINGLE:=New Map<TypeInfo,Variant>
+	
+End

+ 74 - 0
di/DiSetup.monkey2

@@ -0,0 +1,74 @@
+
+Namespace ted2go
+
+' place where all our types are created
+'
+Function SetupDiContainer()
+	
+	Di.Bind( Lambda:DocsTabView()
+		Return New DocsTabView( TabViewFlags.DraggableTabs|TabViewFlags.ClosableTabs )
+	End )
+	
+	Di.Bind<DocBrowserView>()
+	Di.Bind<OutputConsole>()
+	Di.Bind<BuildConsole>()
+	
+	Di.Bind( Lambda:DocumentManager()
+		Return New DocumentManager(
+			Di.Resolve<DocsTabView>(), ' views shouldn't be here!
+			Di.Resolve<DocBrowserView>() )
+	End )
+	
+	Di.Bind( Lambda:ProjectView()
+		Return New ProjectView(
+			Di.Resolve<DocumentManager>(),
+			Di.Resolve<BuildActions>() )
+	End )
+	
+	Di.Bind( Lambda:DebugView()
+		Return New DebugView( 
+			Di.Resolve<DocumentManager>(),
+			Di.Resolve<OutputConsole>() )
+	End )
+	
+	Di.Bind( Lambda:BuildActions()
+		Return New BuildActions( 
+			Di.Resolve<DocumentManager>(),
+			Di.Resolve<BuildConsole>(),
+			Di.Resolve<DebugView>() )
+	End )
+	
+	Di.Bind<HelpView>()
+	
+	Di.Bind( Lambda:HelpTreeView()
+		Local view:=New HelpTreeView(
+			Di.Resolve<HelpView>() )
+		view.Init()
+		Return view
+	End )
+	
+End
+
+
+' some necessarily overhead
+'
+' ideally all classes with business logic must have interfaces
+' and we must works with interfaces
+'
+Class DocsTabView Extends TabViewExt
+	Method New( flags:TabViewFlags=TabViewFlags.DraggableTabs )
+		Super.New( flags )
+	End
+End
+
+Class DocBrowserView Extends DockingView
+End
+
+Class BuildConsole Extends ConsoleExt
+End
+
+Class OutputConsole Extends ConsoleExt
+End
+
+Class HelpView Extends HtmlViewExt
+End

+ 1 - 1
dialog/FindInFilesDialog.monkey2

@@ -57,7 +57,7 @@ Class FindInFilesDialog Extends DialogExt
 			
 			
 			If CustomFolder Return
 			If CustomFolder Return
 			
 			
-			Local projs:=projView.OpenProjects
+			Local projs:=projView.OpenProjectsFolders
 			If Not projs Return
 			If Not projs Return
 			
 			
 			_projList.RemoveAllItems()
 			_projList.RemoveAllItems()

+ 6 - 0
dialog/PrefsDialog.monkey2

@@ -92,6 +92,7 @@ Class PrefsDialog Extends DialogExt
 	Field _mainProjectIcons:CheckButton
 	Field _mainProjectIcons:CheckButton
 	Field _mainProjectSingleClickExpanding:CheckButton
 	Field _mainProjectSingleClickExpanding:CheckButton
 	Field _mainPlaceDocsAtBegin:CheckButton
 	Field _mainPlaceDocsAtBegin:CheckButton
+	Field _mainUseOpenGlEsProfile:CheckButton
 	
 	
 	Field _monkeyRootPath:TextFieldExt
 	Field _monkeyRootPath:TextFieldExt
 	
 	
@@ -147,6 +148,7 @@ Class PrefsDialog Extends DialogExt
 		Prefs.MainProjectIcons=_mainProjectIcons.Checked
 		Prefs.MainProjectIcons=_mainProjectIcons.Checked
 		Prefs.MainProjectSingleClickExpanding=_mainProjectSingleClickExpanding.Checked
 		Prefs.MainProjectSingleClickExpanding=_mainProjectSingleClickExpanding.Checked
 		Prefs.MainPlaceDocsAtBegin=_mainPlaceDocsAtBegin.Checked
 		Prefs.MainPlaceDocsAtBegin=_mainPlaceDocsAtBegin.Checked
+		Prefs.OpenGlProfile=_mainUseOpenGlEsProfile.Checked ? "es" Else ""
 		
 		
 		App.ThemeChanged()
 		App.ThemeChanged()
 		
 		
@@ -174,6 +176,9 @@ Class PrefsDialog Extends DialogExt
 		_mainPlaceDocsAtBegin=New CheckButton( "Place opened document to the left side" )
 		_mainPlaceDocsAtBegin=New CheckButton( "Place opened document to the left side" )
 		_mainPlaceDocsAtBegin.Checked=Prefs.MainPlaceDocsAtBegin
 		_mainPlaceDocsAtBegin.Checked=Prefs.MainPlaceDocsAtBegin
 		
 		
+		_mainUseOpenGlEsProfile=New CheckButton( "Use opengl 'es' mode; uncheck to use full onengl (restart required)" )
+		_mainUseOpenGlEsProfile.Checked=(Prefs.OpenGlProfile="es")
+		
 		_monkeyRootPath=New TextFieldExt( Prefs.MonkeyRootPath )
 		_monkeyRootPath=New TextFieldExt( Prefs.MonkeyRootPath )
 		_monkeyRootPath.Enabled=False
 		_monkeyRootPath.Enabled=False
 		Local chooseMonkeyPath:=New Action( "..." )
 		Local chooseMonkeyPath:=New Action( "..." )
@@ -213,6 +218,7 @@ Class PrefsDialog Extends DialogExt
 		docker.AddView( _mainToolBarVisible,"top" )
 		docker.AddView( _mainToolBarVisible,"top" )
 		docker.AddView( _mainProjectSingleClickExpanding,"top" )
 		docker.AddView( _mainProjectSingleClickExpanding,"top" )
 		docker.AddView( _mainPlaceDocsAtBegin,"top" )
 		docker.AddView( _mainPlaceDocsAtBegin,"top" )
+		docker.AddView( _mainUseOpenGlEsProfile,"top" )
 		docker.AddView( New Label( " " ),"top" )
 		docker.AddView( New Label( " " ),"top" )
 		
 		
 		Return docker
 		Return docker

+ 40 - 131
document/CodeDocument.monkey2

@@ -426,7 +426,7 @@ Class CodeDocumentView Extends Ted2CodeTextView
 							If text.ToLower().EndsWith( "abstract" )
 							If text.ToLower().EndsWith( "abstract" )
 								' nothing
 								' nothing
 							Else
 							Else
-								Local scope:=_doc.Parser.GetScope( FilePath,LineNumAtCursor )
+								Local scope:=_doc.Parser.GetScope( FilePath,CursorPos )
 								If scope And scope.Kind=CodeItemKind.Interface_
 								If scope And scope.Kind=CodeItemKind.Interface_
 									' nothing
 									' nothing
 								Else
 								Else
@@ -759,10 +759,11 @@ Class CodeDocumentView Extends Ted2CodeTextView
 	End
 	End
 	
 	
 	Method ShowJsonDialog()
 	Method ShowJsonDialog()
-	
-		New Fiber( Lambda()
 		
 		
-			Local cmd:="~q"+MainWindow.Mx2ccPath+"~q makeapp -parse -geninfo ~q"+_doc.Path+"~q"
+		Local cmd:=Monkey2Parser.GetParseCommand( _doc.Path )
+		If Not cmd Return
+		
+		New Fiber( Lambda()
 			
 			
 			Local str:=LoadString( "process::"+cmd )
 			Local str:=LoadString( "process::"+cmd )
 			Local i:=str.Find( "{" )
 			Local i:=str.Find( "{" )
@@ -782,7 +783,6 @@ Class CodeDocumentView Extends Ted2CodeTextView
 			tv.Text=str
 			tv.Text=str
 			dock.AddView( tv,"bottom",200,True )
 			dock.AddView( tv,"bottom",200,True )
 			
 			
-			
 			Local dialog:=New Dialog( "ParseInfo",dock )
 			Local dialog:=New Dialog( "ParseInfo",dock )
 			dialog.AddAction( "Close" ).Triggered=dialog.Close
 			dialog.AddAction( "Close" ).Triggered=dialog.Close
 			dialog.MinSize=New Vec2i( 512,600 )
 			dialog.MinSize=New Vec2i( 512,600 )
@@ -1140,7 +1140,7 @@ Class CodeDocument Extends Ted2Document
 						Local s:=textLine.Slice( 0,i1 ).Trim()
 						Local s:=textLine.Slice( 0,i1 ).Trim()
 						s=Utils.GetIndentBeforePos( s,s.Length )
 						s=Utils.GetIndentBeforePos( s,s.Length )
 						
 						
-						Local item:=_parser.ItemAtScope( s,Path,_codeView.LineNumAtCursor )
+						Local item:=_parser.ItemAtScope( s,Path,CursorPos )
 						If item
 						If item
 							' strip ident
 							' strip ident
 							s=item.Text.Slice( item.Ident.Length )
 							s=item.Text.Slice( item.Ident.Length )
@@ -1245,6 +1245,21 @@ Class CodeDocument Extends Ted2Document
 		Return _errMap[line]
 		Return _errMap[line]
 	End
 	End
 	
 	
+	Method OnDocumentParsed( codeItems:Stack<CodeItem>,errors:Stack<BuildError> )
+		
+		ResetErrors()
+		
+		If errors And Not errors.Empty
+			For Local err:=Eachin errors
+				AddError( err )
+			Next
+			Return
+		Endif
+		
+		UpdateCodeTree( codeItems )
+		UpdateFolding()
+	End
+	
 	Method ResetErrors()
 	Method ResetErrors()
 		_errors.Clear()
 		_errors.Clear()
 		_errMap.Clear()
 		_errMap.Clear()
@@ -1266,9 +1281,8 @@ Class CodeDocument Extends Ted2Document
 		
 		
 		If Not _parsingEnabled Return
 		If Not _parsingEnabled Return
 		
 		
-		Local ident:=_codeView.FullIdentAtCursor
-		Local line:=TextDocument.FindLine( _codeView.Cursor )
-		Local item:=_parser.ItemAtScope( ident,Path,line )
+		Local ident:=_codeView.IdentBeforeCursor()
+		Local item:=_parser.ItemAtScope( ident,Path,CursorPos )
 		
 		
 		If item
 		If item
 			Local pos:=item.ScopeStartPos
 			Local pos:=item.ScopeStartPos
@@ -1439,6 +1453,12 @@ Class CodeDocument Extends Ted2Document
 	Field _toolBar:ToolBarExt
 	Field _toolBar:ToolBarExt
 	Field _content:DockingView
 	Field _content:DockingView
 	
 	
+	Method InitParser()
+		
+		_parser=ParsersManager.Get( FileExtension )
+		_parsingEnabled=Not ParsersManager.IsFake( _parser )
+	End
+	  
 	Method GetToolBar:ToolBarExt()
 	Method GetToolBar:ToolBarExt()
 		
 		
 		If _toolBar Return _toolBar
 		If _toolBar Return _toolBar
@@ -1536,8 +1556,6 @@ Class CodeDocument Extends Ted2Document
 		
 		
 		InitParser()
 		InitParser()
 		
 		
-		ParsingDoc() 'start parsing right after loading, not by timer
-		
 		' grab lines after load
 		' grab lines after load
 		_doc.LinesModified+=Lambda( first:Int,removed:Int,inserted:Int )
 		_doc.LinesModified+=Lambda( first:Int,removed:Int,inserted:Int )
 			
 			
@@ -1585,17 +1603,24 @@ Class CodeDocument Extends Ted2Document
 		OnUpdateCurrentScope()
 		OnUpdateCurrentScope()
 	End
 	End
 	
 	
+	Property CursorPos:Vec2i()
+		
+		Return GetCursorPos( _codeView )
+	End
+	
 	Method OnUpdateCurrentScope()
 	Method OnUpdateCurrentScope()
 		
 		
-		Local scope:=_parser.GetNearestScope( Path,_codeView.LineNumAtCursor+1,True )
+		'DebugStop()
+		Local scope:=_parser.GetNearestScope( Path,CursorPos )
+		'Print ""+CursorPos+", "+scope?.KindStr+", "+scope?.Text
 		If scope
 		If scope
 			_treeView.SelectByScope( scope )
 			_treeView.SelectByScope( scope )
 		Endif
 		Endif
 	End
 	End
 	
 	
-	Method UpdateCodeTree()
+	Method UpdateCodeTree( codeItems:Stack<CodeItem> = Null )
 		
 		
-		_treeView.Fill( FileExtension,Path )
+		_treeView.Fill( codeItems,_parser )
 		OnUpdateCurrentScope()
 		OnUpdateCurrentScope()
 	End
 	End
 	
 	
@@ -1639,114 +1664,8 @@ Class CodeDocument Extends Ted2Document
 		Next
 		Next
 	End
 	End
 	
 	
-	Method InitParser()
-		
-		_parser=ParsersManager.Get( FileExtension )
-		_parsingEnabled=Not ParsersManager.IsFake( _parser )
-	End
-	
-	Field _timeDocParsed:=0
-	Field _timeTextChanged:=0
-	Method ParsingOnTextChanged()
-		
-		If Not _parsingEnabled Return
-		
-		_timeTextChanged=Millisecs()
-		
-		If Not _timer Then _timer=New Timer( 1,Lambda()
-		
-			If _parsing Return
-			
-			Local msec:=Millisecs()
-			If msec<_timeDocParsed+1000 Return
-			If _timeTextChanged=0 Or msec<_timeTextChanged+1000 Return
-			_timeTextChanged=0
-			
-			ParsingDoc()
-		
-		End )
-		
-	End
-	
-	Method ParsingDoc()
-		
-		If _parsing Return
-		
-		_parsing=True
-		
-		New Fiber( Lambda()
-			
-			Local dirty:=Dirty
-			
-			Local filePath:=Path
-			If dirty
-				filePath=MainWindow.AllocTmpPath( "_mx2cc_parse_",".monkey2" )
-				SaveString( _doc.Text,filePath )
-			Endif
-			
-			ParsingFile( filePath )
-		
-			If dirty Then DeleteFile( filePath )
-			
-			_parsing=False
-			
-			_timeDocParsed=Millisecs()
-			
-		End )
-		
-	End
-	
-	Method ParsingFile( pathOnDisk:String )
-		
-		If MainWindow.IsTerminating Return
-		
-		ResetErrors()
-		
-		Local errors:=_parser.ParseFile( Path,pathOnDisk,"" )
-		
-		If MainWindow.IsTerminating Return
-		
-		If errors
-			
-			If errors="#"
-				Return
-			Endif
-			
-			Local arr:=errors.Split( "~n" )
-			For Local s:=Eachin arr
-				Local i:=s.Find( "] : Error : " )
-				If i<>-1
-					Local j:=s.Find( " [" )
-					If j<>-1
-						Local path:=s.Slice( 0,j )
-						Local line:=Int( s.Slice( j+2,i ) )-1
-						Local msg:=s.Slice( i+12 )
-						
-						Local err:=New BuildError( path,line,msg )
-					
-						AddError( err )
-						
-					Endif
-				Endif
-			Next
-			
-			Return ' exit when errors
-		Endif
-		
-		UpdateCodeTree()
-		UpdateFolding()
-	End
-	
 	Method OnTextChanged()
 	Method OnTextChanged()
 		
 		
-		' catch for common operations
-		
-		
-		' -----------------------------------
-		' catch for parsing
-		
-		ParsingOnTextChanged()
-		
 	End
 	End
 	
 	
 	Method OnCursorChanged()
 	Method OnCursorChanged()
@@ -1884,9 +1803,8 @@ Class CodeDocument Extends Ted2Document
 			
 			
 			opts.ident=ident
 			opts.ident=ident
 			opts.filePath=Path
 			opts.filePath=Path
-			opts.docLineNum=_codeView.LineNumAtCursor
+			opts.cursor=CursorPos
 			opts.docLineStr=line
 			opts.docLineStr=line
-			opts.docPosInLine=pos
 			opts.results=results
 			opts.results=results
 			
 			
 			results.Clear()
 			results.Clear()
@@ -2375,12 +2293,3 @@ Class ParamsHintView Extends TextView
 	End
 	End
 	
 	
 End
 End
-
-'Class CodeTextView_Bridge Extends CodeTextView Final
-'	
-'	Function OnContentMouseEvent( view:CodeTextView,event:MouseEvent )
-'		
-'		view.OnContentMouseEvent( event )
-'	End
-'	
-'End

+ 90 - 10
document/DocumentManager.monkey2

@@ -8,6 +8,7 @@ Class DocumentManager
 	Field prevDocument:Action
 	Field prevDocument:Action
 
 
 	Field CurrentDocumentChanged:Void()
 	Field CurrentDocumentChanged:Void()
+	Field LockedDocumentChanged:Void()
 	
 	
 	Field DocumentAdded:Void( doc:Ted2Document )
 	Field DocumentAdded:Void( doc:Ted2Document )
 	Field DocumentRemoved:Void( doc:Ted2Document )
 	Field DocumentRemoved:Void( doc:Ted2Document )
@@ -34,22 +35,43 @@ Class DocumentManager
 		nextDocument.Triggered=OnNextDocument
 		nextDocument.Triggered=OnNextDocument
 		nextDocument.HotKey=Key.Tab
 		nextDocument.HotKey=Key.Tab
 		nextDocument.HotKeyModifiers=Modifier.Control
 		nextDocument.HotKeyModifiers=Modifier.Control
-
+		
 		prevDocument=New Action( "Previous tab" )
 		prevDocument=New Action( "Previous tab" )
 		prevDocument.Triggered=OnPrevDocument
 		prevDocument.Triggered=OnPrevDocument
 		prevDocument.HotKey=Key.Tab
 		prevDocument.HotKey=Key.Tab
 		prevDocument.HotKeyModifiers=Modifier.Control|Modifier.Shift
 		prevDocument.HotKeyModifiers=Modifier.Control|Modifier.Shift
 		
 		
+		DocumentRemoved+=Lambda( doc:Ted2Document )
+			
+			If doc=_locked Then OnLockedChanged( Null )
+		End
+		
 		App.Activated+=Lambda()
 		App.Activated+=Lambda()
 			New Fiber( OnAppActivated )
 			New Fiber( OnAppActivated )
 		End
 		End
 	End
 	End
 	
 	
+	Method LockBuildFile()
+	
+		Local doc:=Cast<CodeDocument>( CurrentDocument )
+		OnLockBuildFile( doc )
+	End
+	
+	Property LockedDocument:CodeDocument()
+	
+		Return _locked
+	End
+	
 	Property TabView:TabViewExt()
 	Property TabView:TabViewExt()
-
+	
 		Return _tabView
 		Return _tabView
 	End
 	End
 
 
+	Property CurrentCodeDocument:CodeDocument()
+		
+		Return Cast<CodeDocument>( _currentDoc )
+	End
+	
 	Property CurrentDocument:Ted2Document()
 	Property CurrentDocument:Ted2Document()
 	
 	
 		Return _currentDoc
 		Return _currentDoc
@@ -87,10 +109,20 @@ Class DocumentManager
 	End
 	End
 	
 	
 	Property OpenDocuments:Ted2Document[]()
 	Property OpenDocuments:Ted2Document[]()
-		
+	
 		Return _openDocs.ToArray()
 		Return _openDocs.ToArray()
 	End
 	End
 	
 	
+	Property OpenCodeDocuments:CodeDocument[]()
+		
+		Local stack:=New Stack<CodeDocument>
+		For Local i:=Eachin _openDocs
+			Local doc:=Cast<CodeDocument>( i )
+			If doc Then stack.Add( doc )
+		Next
+		Return stack.ToArray()
+	End
+	
 	Property CurrentDocumentLabel:String()
 	Property CurrentDocumentLabel:String()
 		
 		
 		Return DocumentLabel( CurrentDocument )
 		Return DocumentLabel( CurrentDocument )
@@ -144,7 +176,7 @@ Class DocumentManager
 		
 		
 		doc=docType.CreateDocument( path )
 		doc=docType.CreateDocument( path )
 		If Not doc.Load() Return Null
 		If Not doc.Load() Return Null
-
+		
 		InitDoc( doc )
 		InitDoc( doc )
 		
 		
 		Local addAtBegin:=(openByHand And Prefs.MainPlaceDocsAtBegin)
 		Local addAtBegin:=(openByHand And Prefs.MainPlaceDocsAtBegin)
@@ -255,8 +287,10 @@ Class DocumentManager
 		jobj["openDocuments"]=docs
 		jobj["openDocuments"]=docs
 		
 		
 		If _currentDoc jobj["currentDocument"]=New JsonString( _currentDoc.Path )
 		If _currentDoc jobj["currentDocument"]=New JsonString( _currentDoc.Path )
-	End
 		
 		
+		If _locked jobj["lockedDocument"]=New JsonString( _locked.Path )
+	End
+	
 	Method LoadState( jobj:JsonObject )
 	Method LoadState( jobj:JsonObject )
 		
 		
 		If Not jobj.Contains( "openDocuments" ) Return
 		If Not jobj.Contains( "openDocuments" ) Return
@@ -288,22 +322,33 @@ Class DocumentManager
 			CurrentDocument=_openDocs[0]
 			CurrentDocument=_openDocs[0]
 		Endif
 		Endif
 		
 		
+		If jobj.Contains( "lockedDocument" )
+			Local path:=jobj["lockedDocument"].ToString()
+			OnLockBuildFile( Cast<CodeDocument>( FindDocument( path ) ) )
+		Endif
+		
 	End
 	End
-
+	
 	Method Update()
 	Method Update()
 		
 		
 		nextDocument.Enabled=_openDocs.Length>1
 		nextDocument.Enabled=_openDocs.Length>1
 		prevDocument.Enabled=_openDocs.Length>1
 		prevDocument.Enabled=_openDocs.Length>1
 	End
 	End
 	
 	
+	Method SetAsMainFile( path:String,set:Bool )
+		
+		Local doc:=Cast<CodeDocument>( FindDocument( path ) )
+		If doc Then SetMainFileState( doc,set )
+	End
+	
+	
 	Private
 	Private
 	
 	
 	Field _tabView:TabViewExt
 	Field _tabView:TabViewExt
 	Field _browser:DockingView
 	Field _browser:DockingView
-	
 	Field _currentDoc:Ted2Document
 	Field _currentDoc:Ted2Document
-	
 	Field _openDocs:=New Stack<Ted2Document>
 	Field _openDocs:=New Stack<Ted2Document>
+	Field _locked:CodeDocument
 	
 	
 	Method InitDoc( doc:Ted2Document )
 	Method InitDoc( doc:Ted2Document )
 	
 	
@@ -323,7 +368,7 @@ Class DocumentManager
 		
 		
 			Local index:=_tabView.TabIndex( doc.View )
 			Local index:=_tabView.TabIndex( doc.View )
 			If index=-1 Return	'in case doc already removed via Rename.
 			If index=-1 Return	'in case doc already removed via Rename.
-
+		
 			_tabView.RemoveTab( index )
 			_tabView.RemoveTab( index )
 			_openDocs.Remove( doc )
 			_openDocs.Remove( doc )
 			
 			
@@ -438,7 +483,7 @@ Class DocumentManager
 			Case FileType.None
 			Case FileType.None
 			
 			
 				doc.Dirty=True
 				doc.Dirty=True
-
+				
 				'CurrentDocument=doc
 				'CurrentDocument=doc
 				
 				
 				Alert( "File '"+doc.Path+"' has been deleted!" )
 				Alert( "File '"+doc.Path+"' has been deleted!" )
@@ -449,4 +494,39 @@ Class DocumentManager
 		
 		
 	End
 	End
 	
 	
+	Method OnLockBuildFile( doc:CodeDocument )
+		
+		If Not doc Return
+		
+		If _locked Then SetLockedState( _locked,False )
+		
+		If doc=_locked
+			OnLockedChanged( Null )
+			Return
+		Endif
+		
+		SetLockedState( doc,True )
+		OnLockedChanged( doc )
+	End
+	
+	Method SetLockedState( doc:CodeDocument,locked:Bool )
+	
+		doc.State=locked ? "+" Else ""
+		Local tab:=FindTab( doc.View )
+		If tab Then tab.SetLockedState( locked )
+		CurrentDocumentChanged()
+	End
+	
+	Method SetMainFileState( doc:CodeDocument,state:Bool )
+	
+		doc.State=state ? ">" Else ""
+		CurrentDocumentChanged()
+	End
+	
+	Method OnLockedChanged( locked:CodeDocument )
+		
+		_locked=locked
+		LockedDocumentChanged()
+	End
+	
 End
 End

+ 27 - 27
eventfilter/Monkey2KeyEventFilter.monkey2

@@ -31,33 +31,33 @@ Class Monkey2KeyEventFilter Extends TextViewKeyEventFilter
 					MainWindow.GotoDeclaration()
 					MainWindow.GotoDeclaration()
 					event.Eat()
 					event.Eat()
 				
 				
-				Case Key.F10
-				
-					Print "works"
-					Local doc:=MainWindow.DocsManager.CurrentDocument
-					If Not doc Return
-					
-					New Fiber( Lambda()
-						
-						Local cmd:="~q"+MainWindow.Mx2ccPath+"~q makeapp -parse -geninfo ~q"+doc.Path+"~q"
-						
-						Local str:=LoadString( "process::"+cmd )
-						Local i:=str.Find( "{" )
-						If i=-1 Return
-						str=str.Slice( i )
-						
-						Local jobj:=JsonObject.Parse( str )
-						If Not jobj Return
-						
-						Local jsonTree:=New JsonTreeView( jobj )
-						
-						Local dialog:=New Dialog( "ParseInfo",jsonTree )
-						dialog.AddAction( "Close" ).Triggered=dialog.Close
-						dialog.MinSize=New Vec2i( 512,600 )
-						
-						dialog.Open()
-						
-					End )
+'				Case Key.F10
+'				
+'					Print "works"
+'					Local doc:=MainWindow.DocsManager.CurrentDocument
+'					If Not doc Return
+'					
+'					New Fiber( Lambda()
+'						
+'						Local cmd:=Monkey2Parser.GetParseCommand( doc.Path )
+'						
+'						Local str:=LoadString( "process::"+cmd )
+'						Local i:=str.Find( "{" )
+'						If i=-1 Return
+'						str=str.Slice( i )
+'						
+'						Local jobj:=JsonObject.Parse( str )
+'						If Not jobj Return
+'						
+'						Local jsonTree:=New JsonTreeView( jobj )
+'						
+'						Local dialog:=New Dialog( "ParseInfo",jsonTree )
+'						dialog.AddAction( "Close" ).Triggered=dialog.Close
+'						dialog.MinSize=New Vec2i( 512,600 )
+'						
+'						dialog.Open()
+'						
+'					End )
 			End
 			End
 			
 			
 			
 			

+ 1 - 0
parser/CodeItem.monkey2

@@ -491,6 +491,7 @@ Struct CodeParam
 	End
 	End
 	Field type:CodeType
 	Field type:CodeType
 	Field params:CodeParam[] 'for func as param
 	Field params:CodeParam[] 'for func as param
+	Field srcpos:Vec2i
 	
 	
 	Method ToString:String()
 	Method ToString:String()
 		
 		

+ 331 - 0
parser/CodeParsing.monkey2

@@ -0,0 +1,331 @@
+
+Namespace ted2go
+
+
+Class CodeParsing
+	
+	Method New( docs:DocumentManager,projView:ProjectView )
+		
+		_docsManager=docs
+		
+		_docsManager.DocumentAdded+=Lambda( doc:Ted2Document )
+		
+			Local codeDoc:=Cast<CodeDocument>( doc )
+			If codeDoc
+				StartWatching( codeDoc )
+				
+				codeDoc.Renamed+=Lambda( newPath:String,oldPath:String )
+					
+					' maybe now we have no parser for this file
+					' so re-starting
+					StopWatching( codeDoc )
+					StartWatching( codeDoc )
+				End
+				Return
+			Endif
+			
+'			If ProjectView.IsProjectFile( doc.Path )
+'				
+'			Endif
+			
+		End
+		_docsManager.DocumentRemoved+=Lambda( doc:Ted2Document )
+		
+			Local codeDoc:=Cast<CodeDocument>( doc )
+			If codeDoc Then StopWatching( codeDoc )
+		End
+		_docsManager.LockedDocumentChanged+=Lambda()
+			
+			' have we locked or active path?
+			Local mainFile:=PathsProvider.GetActiveMainFilePath( False )
+			If mainFile
+				FindWatcher( mainFile )?.WakeUp()
+			Else
+				DocWatcher.WakeUpGlobal()
+			Endif
+		End
+		
+		DocWatcher.docsForUpdate=Lambda:CodeDocument[]()
+			
+			Return _docsManager.OpenCodeDocuments
+		End
+		
+		DocWatcher.Init()
+		
+		projView.MainFileChanged+=Lambda( path:String,prevPath:String )
+			
+			DocWatcher.WakeUpGlobal()
+		End
+		
+		projView.ActiveProjectChanged+=Lambda( proj:Monkey2Project )
+			
+			DocWatcher.WakeUpGlobal()
+		End
+	End
+	
+'	Method Parse()
+'		
+'		Local doc:=MainWindow.LockedDocument
+'		If Not doc Then doc=Cast<CodeDocument>( _docsManager.CurrentDocument )
+'		If doc
+'			Local parser:=ParsersManager.Get( doc.CodeView.FileType )
+'			DocWatcher.TryToParse( doc,parser )
+'		Endif
+'	End
+	
+	
+	Private
+	
+	Field _docsManager:DocumentManager
+	Field _watchers:=New Stack<DocWatcher>
+	
+	Method FindWatcher:DocWatcher( doc:CodeDocument )
+		
+		For Local i:=Eachin _watchers
+			If i.doc=doc Return i
+		Next
+		
+		Return Null
+	End
+	
+	Method FindWatcher:DocWatcher( path:String )
+	
+		For Local i:=Eachin _watchers
+			If i.doc.Path=path Return i
+		Next
+	
+		Return Null
+	End
+	
+	Method StartWatching( doc:CodeDocument )
+		
+		If FindWatcher( doc ) Return ' already added
+		
+		Local watcher:=New DocWatcher( doc )
+		_watchers.Add( watcher )
+		
+		watcher.WakeUp()
+	End
+	
+	Method StopWatching( doc:CodeDocument )
+	
+		For Local i:=Eachin _watchers
+			If i.doc=doc
+				i.Dispose()
+				_watchers.Remove( i )
+				Return
+			Endif
+		Next
+	End
+	
+	Method Dispose()
+	
+		DocWatcher.enabled=False
+	End
+	
+End
+
+
+Private
+
+Class DocWatcher
+	
+	Global enabled:=True
+	Field doc:CodeDocument
+	Global docsForUpdate:CodeDocument[]()
+	
+	Method New( doc:CodeDocument )
+		
+		Self.doc=doc
+		_view=doc.CodeView
+		_parser=ParsersManager.Get( _view.FileType )
+		Local canParse:=Not ParsersManager.IsFake( _parser )
+		
+		If canParse
+			_view.Document.TextChanged+=OnTextChanged
+			
+			UpdateDocItems( doc,_parser ) ' refresh parser info for just opened document
+		Endif
+	End
+	
+	Method Dispose()
+		
+		_view.Document.TextChanged-=OnTextChanged
+	End
+	
+	Method WakeUp()
+	
+		OnTextChanged()
+	End
+	
+	Function WakeUpGlobal()
+	
+		_timeTextChanged=Millisecs()
+	End
+	
+	Function Init()
+	
+		_timeTextChanged=Millisecs()
+	End
+	
+	Private
+	
+	Field _view:CodeDocumentView
+	Field _parser:ICodeParser
+	Global _dirtyCounter:=0,_dirtyCounterLastParse:=0
+	Global _timeDocParsed:=0
+	Global _timeTextChanged:=0
+	Global _timer:Timer
+	Global _parsing:Bool
+	Global _changed:=New Stack<CodeDocument>
+	
+	Method OnTextChanged()
+		
+		' skip whitespaces ?
+		'
+'		Local char:=doc.CodeView?.LastTypedChar
+'		Print "char: '"+char+"'"
+
+		TryToParse( doc,_parser )
+	End
+	
+	Function TryToParse( doc:CodeDocument,parser:ICodeParser )
+		
+		_timeTextChanged=Millisecs()
+		
+		If Not _changed.Contains( doc ) Then _changed.Add( doc )
+		
+		If _parsing Return
+		
+		If Not _timer Then _timer=New Timer( 1,Lambda()
+			
+			If _parsing Return
+			
+			Local msec:=Millisecs()
+			If msec<_timeDocParsed+1000 Return
+			If _timeTextChanged=0 Or msec<_timeTextChanged+1000 Return
+			_timeTextChanged=0
+			
+			If Not enabled Return
+			
+			_parsing=True
+			
+			Local docForParsing:CodeDocument
+			Local dirty:=New String[_changed.Length]
+			Local texts:=New String[_changed.Length]
+			For Local i:=0 Until _changed.Length
+				Local d:=_changed[i]
+				If d.Dirty
+					dirty[i]=d.Path
+					texts[i]=d.CodeView.Text
+				Endif
+				docForParsing=d ' grab latest added doc
+			Next
+			_changed.Clear()
+			
+			Local params:=New ParseFileParams
+			params.filePath=PathsProvider.GetActiveMainFilePath()
+			
+			For Local i:=0 Until dirty.Length
+				If dirty[i]
+					dirty[i]=Monkey2Parser.GetTempFilePathForParsing( dirty[i] )
+					SaveString( texts[i],dirty[i] )
+				Endif
+			Next
+			
+			Local errorStr:=parser.ParseFile( params )
+			
+			If Not enabled Return
+			
+			Local errors:=New Stack<BuildError>
+			
+			If errorStr And errorStr<>"#"
+				
+				Local arr:=errorStr.Split( "~n" )
+				For Local s:=Eachin arr
+					Local i:=s.Find( "] : Error : " )
+					If i<>-1
+						Local j:=s.Find( " [" )
+						If j<>-1
+							Local path:=s.Slice( 0,j )
+							Local line:=Int( s.Slice( j+2,i ) )-1
+							Local msg:=s.Slice( i+12 )
+							path=path.Replace( ".mx2/","" )
+							Local err:=New BuildError( path,line,msg )
+							errors.Add( err )
+						Endif
+					Endif
+				Next
+				
+			Endif
+			
+			OnDocumentParsed( docForParsing,parser,errors )
+			
+			For Local i:=0 Until dirty.Length
+				If dirty[i] Then DeleteFile( dirty[i] )
+			Next
+			
+			_parsing=False
+			
+			_timeDocParsed=Millisecs()
+			
+		End )
+		
+	End
+	
+	Function UpdateDocItems( doc:CodeDocument,parser:ICodeParser )
+	
+		Local items:=GetCodeItems( doc.Path,parser )
+		doc.OnDocumentParsed( items,Null )
+	End
+	
+	Function OnDocumentParsed( doc:CodeDocument,parser:ICodeParser,errors:Stack<BuildError> )
+		
+		If doc
+			Local items:=GetCodeItems( doc.Path,parser )
+			doc.OnDocumentParsed( items,GetErrors( doc.Path,errors ) )
+		Endif
+		
+		Local docs:=docsForUpdate()
+		If docs
+			For Local d:=Eachin docs
+				If d=doc Continue
+				Local items:=GetCodeItems( d.Path,parser )
+				d.OnDocumentParsed( items,GetErrors( d.Path,errors ) )
+			Next
+		Endif
+		
+	End
+	
+	Function GetErrors:Stack<BuildError>( path:String,errors:Stack<BuildError>)
+		
+		If errors.Empty Return errors
+		
+		Local st:=New Stack<BuildError>
+		For Local i:=Eachin errors
+			If i.path=path Then st.Add( i )
+		Next
+		Return st
+	End
+	
+	Function GetCodeItems:Stack<CodeItem>( path:String,parser:ICodeParser )
+		
+		Local items:=New Stack<CodeItem>
+		
+		' extract all items in file
+		Local list:=parser.ItemsMap[path]
+		If list Then items.AddAll( list )
+		
+		' extensions are here too
+		For Local lst:=Eachin parser.ExtraItemsMap.Values
+			For Local i:=Eachin lst
+				If i.FilePath=path
+					If Not items.Contains( i.Parent ) Then items.Add( i.Parent )
+				Endif
+			Next
+		Next
+		
+		Return items
+	End
+	
+End

+ 363 - 207
parser/Monkey2Parser.monkey2

@@ -16,9 +16,9 @@ Function FixTypeIdent:String( ident:String )
 	
 	
 	Select ident
 	Select ident
 	Case "new","bool","byte","double","float","int","long","object","short","string","throwable","variant","void","array"
 	Case "new","bool","byte","double","float","int","long","object","short","string","throwable","variant","void","array"
-		Return ident.Slice( 0,1 ).ToUpper()+ident.Slice( 1 )
+		Return Capitalize( ident )
 	Case "cstring","ubyte","uint","ulong","ushort"
 	Case "cstring","ubyte","uint","ulong","ushort"
-		Return ident.Slice( 0,2 ).ToUpper()+ident.Slice( 2 )
+		Return Capitalize( ident,2 )
 	Case "typeinfo"
 	Case "typeinfo"
 		Return "TypeInfo"
 		Return "TypeInfo"
 	End
 	End
@@ -47,7 +47,7 @@ Class Monkey2Parser Extends CodeParserPlugin
 			ParseModules()
 			ParseModules()
 			
 			
 			time=Millisecs()-time
 			time=Millisecs()-time
-			'Print "parse modules: "+time+" ms"
+			Print "completed parse modules: "+(time/1000)+" sec"
 			
 			
 			OnDoneParseModules( time )
 			OnDoneParseModules( time )
 		End )
 		End )
@@ -90,48 +90,57 @@ Class Monkey2Parser Extends CodeParserPlugin
 		Return True
 		Return True
 	End
 	End
 	
 	
-	Method ParseFile:String( filePath:String,pathOnDisk:String,moduleName:String )
+	Method ParseFile:String( params:ParseFileParams )
 		
 		
-		' is file modified?
-		Local time:=GetFileTime( pathOnDisk )
-		If time=0 Return Null ' file not found
-		
-		Local last:=_filesTime[filePath]
-		
-		If last = 0 Or time > last
-			_filesTime[filePath]=time
-			'Print "parse file: "+filePath.Replace( "C:/proj/monkey2/monkey2fork/","" )+"  "+pathOnDisk.Replace( "C:/proj/monkey/monkey2fork/","" )+"  mod:"+Int(isModule)
-		Else
-			'Print "parse file, not modified: "+filePath.Replace( "C:/proj/monkey2/monkey2fork/","" )+"  "+pathOnDisk.Replace( "C:/proj/monkey/monkey2fork/","" )+"  mod:"+Int(isModule)
-			Return Null
-		Endif
+		Local filePath:=params.filePath
+		'Local pathOnDisk:=params.pathOnDisk
+		Local moduleName:=params.moduleName
+		Local geninfo:=params.geninfo
 		
 		
 		' start parsing process
 		' start parsing process
-		Local str:=StartParsing( pathOnDisk )
-		
-		If Not str Return "#" 'special kind of error
+		'
+		If geninfo And _enabled
+			Local parsedPath:String
+			Local cmd:=GetParseCommand( filePath,Varptr parsedPath )
+			
+			If Not cmd Return "#"
+			
+			Local proc:=New ProcessReader( filePath )
+			Local str:=proc.Run( cmd )
+			
+			If Not str Return "#" 'special kind of error
+			
+			Local hasErrors:=(str.Find( "] : Error : " ) > 0)
+			
+			If hasErrors Return str
+			
+			filePath=parsedPath
+		Endif
 		
 		
-'		If Not isModule
-'			Print "-----"
-'			Print str
-'			Print "-----"
-'		Endif
+		Local geninfoPath:=GetGeninfoPath( filePath )
+		If Not geninfo
+			' is file modified?
+			Local time:=GetFileTime( geninfoPath )
+			If time=0 Return Null ' file not found
 		
 		
-		Local hasErrors:=(str.Find( "] : Error : " ) > 0)
+			Local last:=_filesTime[filePath]
 		
 		
-		Local i:=str.Find( "{" )
+			If last = 0 Or time > last
+				_filesTime[filePath]=time
+				'Print "parse file: "+filePath
+			Else
+				'Print "parse file, not modified: "+filePath
+				Return Null
+			Endif
+		Endif
+		'Print "info path: "+geninfoPath
+		Local jobj:=JsonObject.Parse( LoadString( geninfoPath ),True )
 		
 		
-		' return errors
-		If hasErrors Return (i > 0) ? str.Slice( 0,i ) Else str
-		If i=-1 Return "#" ' not a valid json
+		If Not jobj Return "#"
 		
 		
-		'----------
 		
 		
 		RemovePrevious( filePath )
 		RemovePrevious( filePath )
 
 
-		Local json:=str.Slice( i )
-		Local jobj:=JsonObject.Parse( json )
-		
 		Local nspace:= jobj.Contains( "namespace" ) ? jobj["namespace"].ToString() Else ""
 		Local nspace:= jobj.Contains( "namespace" ) ? jobj["namespace"].ToString() Else ""
 		
 		
 		If jobj.Contains( "members" )
 		If jobj.Contains( "members" )
@@ -163,8 +172,8 @@ Class Monkey2Parser Extends CodeParserPlugin
 					Endif
 					Endif
 				Endif
 				Endif
 				file=folder+file
 				file=folder+file
-				'Print "parse import: "+file+"  mod: "+Int(isModule)
-				ParseFile( file,file,moduleName )
+				'Print "parse import: "+file
+				ParseFile( file,moduleName,False )
 			Next
 			Next
 		Endif
 		Endif
 		
 		
@@ -183,17 +192,17 @@ Class Monkey2Parser Extends CodeParserPlugin
 		
 		
 		Return Null
 		Return Null
 	End
 	End
-		
+	
 	Method ParseJsonMembers( members:Stack<JsonValue>,parent:CodeItem,filePath:String,resultContainer:Stack<CodeItem>,namespac:String )
 	Method ParseJsonMembers( members:Stack<JsonValue>,parent:CodeItem,filePath:String,resultContainer:Stack<CodeItem>,namespac:String )
 		
 		
 		For Local val:=Eachin members
 		For Local val:=Eachin members
 		
 		
 			Local jobj:=val.ToObject()
 			Local jobj:=val.ToObject()
 			Local kind:=jobj["kind"].ToString()
 			Local kind:=jobj["kind"].ToString()
-			Local startPos:=jobj["srcpos"].ToString()
-			Local endPos:=jobj["endpos"].ToString()
-			Local flags:=Int( jobj["flags"].ToNumber() )
-			Local ident:=jobj["ident"].ToString()
+			Local flags:=Json_GetInt( jobj,"flags",0 )
+			Local ident:=Json_GetString( jobj,"ident","" )
+			Local srcpos:=GetScopePosition( jobj["srcpos"].ToString() )
+			Local endpos:=GetScopePosition( jobj["endpos"].ToString() )
 			
 			
 			ident=FixTypeIdent( ident )
 			ident=FixTypeIdent( ident )
 			
 			
@@ -211,48 +220,69 @@ Class Monkey2Parser Extends CodeParserPlugin
 			item.KindStr=kind
 			item.KindStr=kind
 			item.Access=GetAccess( flags )
 			item.Access=GetAccess( flags )
 			item.FilePath=filePath
 			item.FilePath=filePath
-			Local arr:=startPos.Split( ":" )
-			item.ScopeStartPos=New Vec2i( Int(arr[0])-1,Int(arr[1]) )
-			arr=endPos.Split( ":" )
-			item.ScopeEndPos=New Vec2i( Int(arr[0])-1,Int(arr[1]) )
+			item.ScopeStartPos=srcpos
+			item.ScopeEndPos=endpos
 			item.Namespac=namespac
 			item.Namespac=namespac
 			item.IsIfaceMember=(flags & Flags.DECL_IFACEMEMBER <> 0)
 			item.IsIfaceMember=(flags & Flags.DECL_IFACEMEMBER <> 0)
 			'Print "parser. add item: "+item.Scope+" "+kind
 			'Print "parser. add item: "+item.Scope+" "+kind
 			
 			
-			If kind="class" Or kind="struct" Or kind="interface" Or kind="enum"
-				Local t:=New CodeType
-				t.kind=kind
-				t.ident=ident
+			Select kind
+				Case "class","struct","interface","enum"
 				
 				
-				item.IsExtension=IsExtension( flags )
-			Else
-				Local t:=ParseType( jobj )
-				item.Type=t
+					item.IsExtension=IsExtension( flags )
+					
+				Case "block"
+					
+					item.Ident=""+item.ScopeStartPos+"..."+item.ScopeEndPos
 				
 				
-				' params
-				If t.kind="functype"
-					Local params:=ParseParams( jobj )
-					If params
-						item.Params=params
-						' add params as children
-						For Local p:=Eachin params
-							Local i:=New CodeItem( p.ident )
-							i.Type=p.type
-							i.KindStr="param"
-							i.Parent=item
-							i.ScopeStartPos=item.ScopeStartPos
-							i.FilePath=item.FilePath
-						Next
+				Case "property"
+					
+					Local t:=ParseType( jobj )
+					item.Type=t
+					
+					If jobj.Contains( "getFunc" )
+						Local getFunc:=jobj["getFunc"].ToObject()
+						item.ScopeStartPos=GetScopePosition( getFunc["srcpos"].ToString() )
+						item.ScopeEndPos=GetScopePosition( getFunc["endpos"].ToString() )
+						If getFunc.Contains( "stmts" )
+							Local memb:=getFunc["stmts"].ToArray()
+							ParseJsonMembers( memb,item,filePath,resultContainer,namespac )
+						Endif
 					Endif
 					Endif
-				Endif
-				
-				' alias
-				If kind = "alias"
-					_aliases.Add( ident,item )
-					item.isAlias=True
-				End
-				
-			Endif
+					If jobj.Contains( "setFunc" )
+						Local setFunc:=jobj["setFunc"].ToObject()
+						item.ScopeStartPos=GetScopePosition( setFunc["srcpos"].ToString() )
+						item.ScopeEndPos=GetScopePosition( setFunc["endpos"].ToString() )
+						Local params:=ParseParams( setFunc )
+						InsertParams( item,params )
+						If setFunc.Contains( "stmts" )
+							Local memb:=setFunc["stmts"].ToArray()
+							ParseJsonMembers( memb,item,filePath,resultContainer,namespac )
+						Endif
+					Endif
+					
+					item.ScopeStartPos=srcpos
+					item.ScopeEndPos=endpos
+					
+				Default
+					
+					Local t:=ParseType( jobj )
+					item.Type=t
+					
+					' params
+					If t.kind="functype"
+						Local params:=ParseParams( jobj )
+						InsertParams( item,params )
+					Endif
+					
+					' alias
+					If kind="alias"
+						_aliases.Add( ident,item )
+						item.isAlias=True
+					Elseif kind="local"
+						item.ScopeEndPos=parent.ScopeEndPos
+					End
+			End
 			
 			
 			If jobj.Contains( "superType" )
 			If jobj.Contains( "superType" )
 				Local sup:=jobj["superType"].ToObject()
 				Local sup:=jobj["superType"].ToObject()
@@ -269,7 +299,12 @@ Class Monkey2Parser Extends CodeParserPlugin
 				Next
 				Next
 			Endif
 			Endif
 			
 			
-			If parent
+			If kind="local"
+				' add into parent that isn't a nested block
+				' like method/func
+				Local par:=GetNonBlockParent( parent )
+				item.SetParent( par )
+			Elseif parent
 				item.SetParent( parent )
 				item.SetParent( parent )
 				If parent.IsExtension
 				If parent.IsExtension
 					AddExtensionItem( parent,item )
 					AddExtensionItem( parent,item )
@@ -280,6 +315,15 @@ Class Monkey2Parser Extends CodeParserPlugin
 				Endif
 				Endif
 			Endif
 			Endif
 			
 			
+			' local members and blocks like if/switch/etc..
+			'
+			If jobj.Contains( "stmts" )
+				Local memb:=jobj["stmts"].ToArray()
+				ParseJsonMembers( memb,item,filePath,resultContainer,namespac )
+			Endif
+			
+			' inner members
+			'
 			If jobj.Contains( "members" )
 			If jobj.Contains( "members" )
 				Local memb:=jobj["members"].ToArray()
 				Local memb:=jobj["members"].ToArray()
 				ParseJsonMembers( memb,item,filePath,resultContainer,namespac )
 				ParseJsonMembers( memb,item,filePath,resultContainer,namespac )
@@ -298,83 +342,34 @@ Class Monkey2Parser Extends CodeParserPlugin
 		Return Not IsPosInsideOfQuotes( line,posInLine )
 		Return Not IsPosInsideOfQuotes( line,posInLine )
 	End
 	End
 	
 	
-	Method GetScope:CodeItem( docPath:String,docLine:Int )
-		
-		Local items:=ItemsMap[docPath]
+	Method GetScope:CodeItem( docPath:String,cursor:Vec2i )
 		
 		
-		If Not items Return Null
-		
-		' all classes / structs
-		Local result:CodeItem=Null
-		For Local i:=Eachin items
-			If docLine > i.ScopeStartPos.x And docLine < i.ScopeEndPos.x
-				result=i
-				Exit
-			Endif
-		Next
+		Local result:=GetNearestScope( docPath,cursor )
 		
 		
-		If result
-			' try to check - are we inside of method/ property / etc
-			Repeat
-				Local i:=GetInnerScope( result,docLine )
-				If i = Null Exit
-				result=i
-			Forever
-			
-		Else
-			' try to find in extension members
-			For Local list:=Eachin ExtraItemsMap.Values.All()
-				For Local i:=Eachin list
-					If i.FilePath<>docPath Continue
-					If docLine > i.ScopeStartPos.x And docLine < i.ScopeEndPos.x
-						result=i
-						Exit
-					Endif
-				Next
-				If result Exit
-			Next
-		End
+		' we are looking for scope here, so skip locals
+		'
+		If result And IsLocalMember( result )
+			result=result.Parent
+		Endif
 		
 		
 		Return result
 		Return result
 		
 		
 	End
 	End
 	
 	
-	Method GetNearestScope:CodeItem( docPath:String,docLine:Int,above:Bool )
+	Method GetNearestScope:CodeItem( docPath:String,cursor:Vec2i )
 	
 	
 		Local items:=ItemsMap[docPath]
 		Local items:=ItemsMap[docPath]
-	
+		
 		If Not items Return Null
 		If Not items Return Null
-	
+		
 		' all classes / structs
 		' all classes / structs
-		Local fakeItem:=New CodeItem( "fake" )
-		fakeItem.Children=items
-'		For Local i:=Eachin items
-'			If docLine > i.ScopeStartPos.x And docLine < i.ScopeEndPos.x
-'				result=i
-'				Exit
-'			Endif
-'		Next
-		Local dir:=above ? -1 Else 1
-		Local result:=GetInnerScope( fakeItem,docLine )
-		
-		If result
-			' try to check - are we inside of method/ property / etc
-			Repeat
-				Local i:=GetInnerScope( result,docLine,dir )
-				If i = Null Exit
-				result=i
-			Forever
-	
-		Else
+		Local result:=GetInnerScope( items,cursor )
+		
+		If Not result
 			' try to find in extension members
 			' try to find in extension members
-			For Local list:=Eachin ExtraItemsMap.Values
-				For Local i:=Eachin list
-					If i.FilePath<>docPath Continue
-					If docLine > i.ScopeStartPos.x And docLine < i.ScopeEndPos.x
-						result=i
-						Exit
-					Endif
-				Next
+			For Local list:=Eachin ExtraItemsMap.Values.All()
+				If list.Empty Or list[0].FilePath<>docPath Continue
+				Local result:=GetInnerScope( list,cursor )
 				If result Exit
 				If result Exit
 			Next
 			Next
 		End
 		End
@@ -383,13 +378,13 @@ Class Monkey2Parser Extends CodeParserPlugin
 	
 	
 	End
 	End
 	
 	
-	Method ItemAtScope:CodeItem( ident:String,filePath:String,docLine:Int )
+	Method ItemAtScope:CodeItem( ident:String,filePath:String,cursor:Vec2i )
 		
 		
 		Local opts:=New ParserRequestOptions
 		Local opts:=New ParserRequestOptions
 		opts.results=New Stack<CodeItem>
 		opts.results=New Stack<CodeItem>
 		opts.ident=ident
 		opts.ident=ident
 		opts.filePath=filePath
 		opts.filePath=filePath
-		opts.docLineNum=docLine
+		opts.cursor=cursor
 		opts.usingsFilter=_lastUsingsFilter
 		opts.usingsFilter=_lastUsingsFilter
 		opts.intelliIdent=False
 		opts.intelliIdent=False
 		
 		
@@ -444,9 +439,60 @@ Class Monkey2Parser Extends CodeParserPlugin
 		
 		
 	End
 	End
 	
 	
+	Function GetParseCommand:String( filePathToParse:String,realParsedPath:String Ptr=Null )
+		
+		Local path:String
+		' is it a module file?
+		Local modsDir:=Prefs.MonkeyRootPath+"modules/"
+		If filePathToParse.StartsWith( modsDir ) And filePathToParse.Find( "/tests/")=-1
+			Local i1:=modsDir.Length
+			Local i2:=filePathToParse.Find( "/",i1+1 )
+			If i2<>-1
+				Local modName:=filePathToParse.Slice( i1,i2 )
+				path=modsDir+modName+"/"+modName+".monkey2"
+			Else
+				path=filePathToParse
+			Endif
+		Else
+			Local mainFile:=PathsProvider.GetActiveMainFilePath()
+			If mainFile
+				If GetFileType( mainFile )<>FileType.File
+					Alert( "File doesn't exists!~n"+mainFile,"Invalid main file" )
+				Endif
+				' is it a standalone file?
+				Local proj:=ProjectView.FindProject( filePathToParse )?.Folder
+				path = mainFile.StartsWith( proj ) ? mainFile Else filePathToParse
+			Else
+				path=filePathToParse
+			Endif
+		Endif
+		Print "path: "+path
+		realParsedPath[0]=path
+		
+		Return path ? "~q"+MainWindow.Mx2ccPath+"~q geninfo ~q"+path+"~q" Else ""
+	End
+	
+	Function GetGeninfoPath:String( filePath:String )
+		
+		Return ExtractDir( filePath )+".mx2/"+StripDir( StripExt( filePath ) )+".geninfo"
+	End
+	
+	Function GetTempFilePathForParsing:String( srcPath:String )
+		
+		Local dir:=ExtractDir( srcPath )+".mx2/"
+		CreateDir( dir )
+		Local name:=StripDir( srcPath )
+		
+		Return dir+name
+	End
+	
 	
 	
 	Private
 	Private
 	
 	
+	Const LOCAL_RULE_NONE:=0
+	Const LOCAL_RULE_SELF_SCOPE:=1
+	Const LOCAL_RULE_PARENT_SCOPE:=2
+	
 	Global _instance:=New Monkey2Parser
 	Global _instance:=New Monkey2Parser
 	Field _filesTime:=New StringMap<Long>
 	Field _filesTime:=New StringMap<Long>
 	Field _aliases:=New StringMap<CodeItem>
 	Field _aliases:=New StringMap<CodeItem>
@@ -464,7 +510,7 @@ Class Monkey2Parser Extends CodeParserPlugin
 		
 		
 		Local ident:=options.ident
 		Local ident:=options.ident
 		Local filePath:=options.filePath
 		Local filePath:=options.filePath
-		Local docLineNum:=options.docLineNum
+		Local cursor:=options.cursor
 		Local docLineStr:=options.docLineStr
 		Local docLineStr:=options.docLineStr
 		Local target:=options.results
 		Local target:=options.results
 		Local usingsFilter:=options.usingsFilter
 		Local usingsFilter:=options.usingsFilter
@@ -479,10 +525,10 @@ Class Monkey2Parser Extends CodeParserPlugin
 		Local onlyOne:=(idents.Length=1)
 		Local onlyOne:=(idents.Length=1)
 	
 	
 		'check current scope
 		'check current scope
-		Local rootScope:=GetScope( filePath,docLineNum )
+		Local rootScope:=GetScope( filePath,cursor )
 		Local scope:=rootScope
 		Local scope:=rootScope
 		
 		
-		'If scope Print scope.Text
+		'Print "scope: "+scope?.Text
 		
 		
 		'-----------------------------
 		'-----------------------------
 		' what the first ident is?
 		' what the first ident is?
@@ -520,12 +566,12 @@ Class Monkey2Parser Extends CodeParserPlugin
 							'Print "cont1: "+i.Ident
 							'Print "cont1: "+i.Ident
 							Continue
 							Continue
 						Endif
 						Endif
-						If Not CheckAccessInScope( i,scope )
+						If Not CheckAccessInScope( scope,i )
 							'Print "cont2: "+i.Ident
 							'Print "cont2: "+i.Ident
 							Continue
 							Continue
 						Endif
 						Endif
 						' additional checking for the first ident
 						' additional checking for the first ident
-						If IsLocalMember( i ) And i.ScopeStartPos.x > docLineNum
+						If IsLocalMember( i ) And Not CheckLineLocation( i,cursor,LOCAL_RULE_PARENT_SCOPE )
 							'Print "cont3: "+i.Ident
 							'Print "cont3: "+i.Ident
 							Continue
 							Continue
 						Endif
 						Endif
@@ -581,26 +627,28 @@ Class Monkey2Parser Extends CodeParserPlugin
 				
 				
 				For Local i:=Eachin Items
 				For Local i:=Eachin Items
 					
 					
+					'Local similar:=i.Ident.ToLower().StartsWith( ident )
+					
 					If Not CheckUsingsFilter( i.Namespac,usingsFilter )
 					If Not CheckUsingsFilter( i.Namespac,usingsFilter )
-						'Print "skip 1 "+i.Ident
+						'If similar Print "exclude: "+i.Namespac+" "+i.Text
 						Continue
 						Continue
 					Endif
 					Endif
 					
 					
 					'Print "global 1: "+i.Scope
 					'Print "global 1: "+i.Scope
 					If Not CheckIdent( i.Ident,firstIdent,onlyOne,intelliIdent )
 					If Not CheckIdent( i.Ident,firstIdent,onlyOne,intelliIdent )
-						'Print "skip 2 "+i.Ident
+						'If similar Print "skip 2 "+i.Ident
 						Continue
 						Continue
 					Endif
 					Endif
 					
 					
 					If Not CheckAccessInGlobal( i,filePath )
 					If Not CheckAccessInGlobal( i,filePath )
-						'Print "skip 3 "+i.Ident
+						'If similar Print "skip 3 "+i.Ident
 						Continue
 						Continue
 					Endif
 					Endif
 					
 					
-					If IsLocalMember( i ) And i.ScopeStartPos.x > docLineNum
-						'Print "skip 4 "+i.Ident
-						Continue
-					Endif
+'					If Not CheckLineLocation( i,cursor )
+'						If similar Print "skip 4 "+i.Ident
+'						Continue
+'					Endif
 					
 					
 					'Print "global 2"
 					'Print "global 2"
 					If Not onlyOne
 					If Not onlyOne
@@ -635,7 +683,7 @@ Class Monkey2Parser Extends CodeParserPlugin
 		If item = Null
 		If item = Null
 			
 			
 			Local s:=ident
 			Local s:=ident
-			If s.EndsWith( "." ) Then s=s.Slice( 0,s.Length-1 )
+			If s.EndsWith( "." ) Then s=s.Slice( 0,-1 )
 			Local tuple:=NSpace.Find( s,False,usingsFilter )
 			Local tuple:=NSpace.Find( s,False,usingsFilter )
 			Local ns := tuple ? (tuple.Item1 ? tuple.Item1 Else tuple.Item2) Else Null
 			Local ns := tuple ? (tuple.Item1 ? tuple.Item1 Else tuple.Item2) Else Null
 			If ns
 			If ns
@@ -791,12 +839,15 @@ Class Monkey2Parser Extends CodeParserPlugin
 		
 		
 		Local al:=_aliases[typeName]
 		Local al:=_aliases[typeName]
 		Return al ? al.Type.ident Else typeName
 		Return al ? al.Type.ident Else typeName
+		
 	End
 	End
 	
 	
 	Method AddExtensionItem( parent:CodeItem,item:CodeItem )
 	Method AddExtensionItem( parent:CodeItem,item:CodeItem )
 		
 		
 		Local key:=parent.Ident
 		Local key:=parent.Ident
+		
 		Local list:=ExtraItemsMap[key]
 		Local list:=ExtraItemsMap[key]
+		
 		If Not list
 		If Not list
 			list=New Stack<CodeItem>
 			list=New Stack<CodeItem>
 			ExtraItemsMap[key]=list
 			ExtraItemsMap[key]=list
@@ -807,6 +858,7 @@ Class Monkey2Parser Extends CodeParserPlugin
 				Exit
 				Exit
 			Endif
 			Endif
 		Next
 		Next
+		
 		item.IsExtension=True
 		item.IsExtension=True
 		list.Add( item )
 		list.Add( item )
 	End
 	End
@@ -832,16 +884,14 @@ Class Monkey2Parser Extends CodeParserPlugin
 		ted2go.ExtractExtensionItems( _extensions,item,target )
 		ted2go.ExtractExtensionItems( _extensions,item,target )
 	End
 	End
 	
 	
-	Method StartParsing:String( pathOnDisk:String )
-		
-		If Not _enabled Return ""
-		
-		Local proc:=New ProcessReader( pathOnDisk )
+	Method ParseFile( path:String,moduleName:String,geninfo:Bool=True )
 		
 		
-		Local cmd:=_mx2ccPath+" makeapp -parse -geninfo ~q"+pathOnDisk+"~q"
-		Local str:=proc.Run( cmd )
+		Local params:=New ParseFileParams
+		params.filePath=path
+		params.moduleName=moduleName
+		params.geninfo=geninfo
 		
 		
-		Return str
+		ParseFile( params )
 	End
 	End
 	
 	
 	Method ParseModules()
 	Method ParseModules()
@@ -852,6 +902,7 @@ Class Monkey2Parser Extends CodeParserPlugin
 		
 		
 		' pop up some modules to parse them first
 		' pop up some modules to parse them first
 		Local dirs:=New Stack<String>
 		Local dirs:=New Stack<String>
+		
 		dirs.AddAll( dd )
 		dirs.AddAll( dd )
 		Local mods:=New String[]( "std","mojo","monkey" )
 		Local mods:=New String[]( "std","mojo","monkey" )
 		For Local m:=Eachin mods
 		For Local m:=Eachin mods
@@ -865,16 +916,79 @@ Class Monkey2Parser Extends CodeParserPlugin
 				'Print "module: "+file
 				'Print "module: "+file
 				If GetFileType( file )=FileType.File
 				If GetFileType( file )=FileType.File
 					OnParseModule( file )
 					OnParseModule( file )
-					ParseFile( file,file,d )
+					ParseFile( file,d )
 				Endif
 				Endif
 			Endif
 			Endif
 		Next
 		Next
 		
 		
 	End
 	End
 	
 	
+	Method StripNamespace:String( type:String )
+		
+		Local pair:=NSpace.Find( type,False )
+		
+		Local nspace:=pair?.Item2?.FullName
+		If nspace
+			type=type.Replace( nspace+".","" )
+		Endif
+		
+		Return type
+	End
+	
+	Method ParseSemtype:CodeType( semtype:String,kind:String )
+		
+		'DebugStop()
+		
+		semtype=StripNamespace( semtype )
+		semtype=semtype.Replace( "monkey.types.","" )
+		
+		Local type:=New CodeType
+		
+		If semtype.EndsWith( "[]" )
+			type.isArray=True
+			semtype=semtype.Slice( 0,-2 )
+		Endif
+		
+		' generics
+		'
+		Local i:=semtype.Find( "<" )
+		Local args:CodeType[]
+		If i<>-1
+			Local generic:=semtype.Slice( i+1,-1 )
+			semtype=semtype.Slice( 0,i )
+			
+			Local parts:=generic.Split( "," )
+			For Local part:=Eachin parts
+				Local stripped:=StripNamespace( part )
+				If stripped<>part
+					generic=generic.Replace( part,stripped )
+				Endif
+			Next
+			
+			Local t:=New CodeType
+			t.ident=generic
+			args=New CodeType[]( t )
+		Endif
+		
+		type.ident=semtype
+		type.expr=semtype
+		type.kind=kind
+		type.args=args
+		
+		Return type
+	End
+	
 	Method ParseType:CodeType( jobj:Map<String,JsonValue>,type:Map<String,JsonValue> = Null )
 	Method ParseType:CodeType( jobj:Map<String,JsonValue>,type:Map<String,JsonValue> = Null )
 		
 		
-		If type=Null Then type=GetJobjType( jobj )
+		If type=Null
+			
+			Local semtype:=Json_GetString( jobj,"semtype","" )
+			If semtype
+				Return ParseSemtype( semtype,Json_GetString( jobj,"kind","" ) )
+			Endif
+			
+			type=GetJobjType( jobj )
+		Endif
 		
 		
 		If Not type
 		If Not type
 		
 		
@@ -1032,6 +1146,7 @@ Class Monkey2Parser Extends CodeParserPlugin
 			Local jparam:=param.ToObject()
 			Local jparam:=param.ToObject()
 			Local p:=New CodeParam
 			Local p:=New CodeParam
 			p.ident=jparam["ident"].ToString()
 			p.ident=jparam["ident"].ToString()
+			p.srcpos=GetScopePosition( jparam["srcpos"].ToString() )
 			p.type=ParseType( jparam )
 			p.type=ParseType( jparam )
 			' try recursive extraction
 			' try recursive extraction
 			p.params=ParseParams( jparam )
 			p.params=ParseParams( jparam )
@@ -1080,43 +1195,25 @@ Class Monkey2Parser Extends CodeParserPlugin
 		Return type
 		Return type
 	End
 	End
 	
 	
-	Method GetInnerScope:CodeItem( parent:CodeItem,docLine:Int,dir:Int=0 )
+	Method GetInnerScope:CodeItem( items:Stack<CodeItem>,cursor:Vec2i )
 		
 		
-		Local items:=parent.Children
 		If items=Null Return Null
 		If items=Null Return Null
 		
 		
 		Local result:CodeItem=Null
 		Local result:CodeItem=Null
-		Local storedPos:=dir>0 ? 999999999 Else -1
 		For Local i:=Eachin items
 		For Local i:=Eachin items
-			
-			If i.Kind=CodeItemKind.Param_ Continue
-			
-			Local i1:=i.ScopeStartPos.x
-			Local i2:=i.ScopeEndPos.x
-			
-			'inside
-			If docLine>i1 And docLine<=i2
+			If CheckLineLocation( i,cursor,LOCAL_RULE_SELF_SCOPE )
 				result=i
 				result=i
-				Exit
-			Endif
-			
-			If dir=-1 ' above
-				
-				If docLine>i2 And i2>storedPos
-					result=i
-					storedPos=i2
-				Endif
-				
-			Elseif dir=1 ' below
-				
-				If docLine<i1 And i1<storedPos
-					result=i
-					storedPos=i1
+				If Not IsLocalMember( i )
+					items=result.Children
+					If items ' check all nested
+						i=GetInnerScope( items,cursor )
+						If i<>Null Then result=i
+					Endif
+					Exit
 				Endif
 				Endif
-				
-			End
-			
+			Endif
 		Next
 		Next
+		
 		Return result
 		Return result
 	End
 	End
 	
 	
@@ -1137,6 +1234,39 @@ Class Monkey2Parser Extends CodeParserPlugin
 		Return False
 		Return False
 	End
 	End
 	
 	
+	Function GetNonBlockParent:CodeItem( parent:CodeItem )
+	
+		Local par:=parent
+		While par And par.KindStr="block"
+			par=par.Parent
+		Wend
+		
+		Return par
+	End
+	
+	Function GetScopePosition:Vec2i( strPos:String )
+		
+		Local arr:=strPos.Split( ":" )
+		Return New Vec2i( Int(arr[0])-1,Int(arr[1]) )
+	End
+	
+	Function InsertParams( item:CodeItem,params:CodeParam[] )
+		
+		If params
+			item.Params=params
+			' add params as children
+			For Local p:=Eachin params
+				Local i:=New CodeItem( p.ident )
+				i.Type=p.type
+				i.KindStr="param"
+				i.Parent=item
+				i.ScopeStartPos=p.srcpos
+				i.ScopeEndPos=item.ScopeEndPos
+				i.FilePath=item.FilePath
+			Next
+		Endif
+	End
+	
 	Method GetAllItems( item:CodeItem,target:Stack<CodeItem>,isSuper:Bool=False )
 	Method GetAllItems( item:CodeItem,target:Stack<CodeItem>,isSuper:Bool=False )
 		
 		
 		Local checkUnique:=Not target.Empty
 		Local checkUnique:=Not target.Empty
@@ -1171,7 +1301,7 @@ Class Monkey2Parser Extends CodeParserPlugin
 		
 		
 	End
 	End
 	
 	
-	Method CheckAccessInScope:Bool( item:CodeItem,parent:CodeItem )
+	Method CheckAccessInScope:Bool( parent:CodeItem,item:CodeItem )
 		
 		
 		' always show public members
 		' always show public members
 		Local a:=item.Access
 		Local a:=item.Access
@@ -1237,6 +1367,31 @@ Class Monkey2Parser Extends CodeParserPlugin
 		
 		
 	End
 	End
 	
 	
+	Method CheckLineLocation:Bool( item:CodeItem,cursor:Vec2i,localRule:Int=LOCAL_RULE_NONE )
+		
+		Local srcpos:=item.ScopeStartPos
+		Local endpos:=item.ScopeEndPos
+		
+		If localRule<>LOCAL_RULE_NONE And IsLocalMember( item )
+			'cursor.x-=1 ' hacking
+			If localRule=LOCAL_RULE_SELF_SCOPE
+				Return cursor.x=srcpos.x And cursor.y>=srcpos.y
+			Elseif localRule=LOCAL_RULE_PARENT_SCOPE
+				Return (cursor.x=srcpos.x And cursor.y>=srcpos.y) Or 
+						(cursor.x>srcpos.x And cursor.x<endpos.x)
+			Endif
+		Else
+			endpos.x+=1
+			If cursor.x>srcpos.x And cursor.x<endpos.x
+				Return True
+			Elseif cursor.x=srcpos.x Or cursor.x=endpos.x
+				Return cursor.y>=srcpos.y And cursor.y<=endpos.y
+			Endif
+		Endif
+	
+		Return False
+	End
+	
 	Method CheckIdent:Bool( ident1:String,ident2:String,startsOnly:Bool,intelliIdent:Bool=True )
 	Method CheckIdent:Bool( ident1:String,ident2:String,startsOnly:Bool,intelliIdent:Bool=True )
 	
 	
 		If ident2="" Return True
 		If ident2="" Return True
@@ -1275,8 +1430,8 @@ Class Monkey2Parser Extends CodeParserPlugin
 	
 	
 	Function GetAccess:AccessMode( flags:Int )
 	Function GetAccess:AccessMode( flags:Int )
 		
 		
-		If flags & Flags.DECL_PRIVATE Return AccessMode.Private_
-		If flags & Flags.DECL_PROTECTED Return AccessMode.Protected_
+		If flags & Flags.DECL_PRIVATE <> 0 Return AccessMode.Private_
+		If flags & Flags.DECL_PROTECTED <> 0 Return AccessMode.Protected_
 		Return AccessMode.Public_
 		Return AccessMode.Public_
 	End
 	End
 	
 	
@@ -1428,13 +1583,14 @@ Class NSpace
 		Next
 		Next
 		
 		
 		Local ns:NSpace
 		Local ns:NSpace
-		For Local n:=Eachin list
-			If n And Monkey2Parser.CheckUsingsFilter( n.FullName,usingsFilter )
-				ns=n
-				Exit
-			Endif
-		Next
-		
+		If usingsFilter
+			For Local n:=Eachin list
+				If n And Monkey2Parser.CheckUsingsFilter( n.FullName,usingsFilter )
+					ns=n
+					Exit
+				Endif
+			Next
+		Endif
 		Return New Tuple2<NSpace,NSpace>( ns,lastNs )
 		Return New Tuple2<NSpace,NSpace>( ns,lastNs )
 		
 		
 	End
 	End
@@ -1506,7 +1662,7 @@ Function GetLiteralType:String( typeIdent:String )
 		Return "Float"
 		Return "Float"
 	Else
 	Else
 		typeIdent=typeIdent.ToLower()
 		typeIdent=typeIdent.ToLower()
-		If typeIdent = "true" Or typeIdent = "false" Return "Bool"
+		If typeIdent="true" Or typeIdent="false" Return "Bool"
 	Endif
 	Endif
 	Return ""
 	Return ""
 End
 End

+ 18 - 10
parser/Parser.monkey2

@@ -6,13 +6,13 @@ Interface ICodeParser
 	
 	
 	Method GetConstructors( item:CodeItem,target:Stack<CodeItem> )
 	Method GetConstructors( item:CodeItem,target:Stack<CodeItem> )
 	Method RefineRawType( item:CodeItem )
 	Method RefineRawType( item:CodeItem )
-	Method ParseFile:String( filePath:String,pathOnDisk:String,moduleName:String )
+	Method ParseFile:String( params:ParseFileParams )
 	'Method ParseJson( json:String,filePath:String )
 	'Method ParseJson( json:String,filePath:String )
 	Method IsPosInsideOfQuotes:Bool( text:String,pos:Int )
 	Method IsPosInsideOfQuotes:Bool( text:String,pos:Int )
 	Method CanShowAutocomplete:Bool( line:String,posInLine:Int )
 	Method CanShowAutocomplete:Bool( line:String,posInLine:Int )
-	Method GetScope:CodeItem( docPath:String,docLine:Int )
-	Method GetNearestScope:CodeItem( docPath:String,docLine:Int,above:Bool )
-	Method ItemAtScope:CodeItem( ident:String,filePath:String,docLine:Int )
+	Method GetScope:CodeItem( docPath:String,cursor:Vec2i )
+	Method GetNearestScope:CodeItem( docPath:String,cursor:Vec2i )
+	Method ItemAtScope:CodeItem( ident:String,filePath:String,cursor:Vec2i )
 	
 	
 	Method GetItemsForAutocomplete( options:ParserRequestOptions )
 	Method GetItemsForAutocomplete( options:ParserRequestOptions )
 	Method CheckStartsWith:Bool( ident1:String,ident2:String )
 	Method CheckStartsWith:Bool( ident1:String,ident2:String )
@@ -112,15 +112,23 @@ Class ParserRequestOptions Final
 	
 	
 	Field ident:String
 	Field ident:String
 	Field filePath:String
 	Field filePath:String
-	Field docLineNum:Int
+	Field cursor:Vec2i
 	Field results:Stack<CodeItem>
 	Field results:Stack<CodeItem>
 	Field usingsFilter:Stack<String>
 	Field usingsFilter:Stack<String>
 	Field docLineStr:String
 	Field docLineStr:String
-	Field docPosInLine:Int
 	Field intelliIdent:=True
 	Field intelliIdent:=True
 	
 	
 End
 End
 
 
+Class ParseFileParams
+	
+	Field filePath:String
+	'Field paths:String[]
+	Field moduleName:String
+	Field geninfo:Bool=True
+	
+End
+
 
 
 Private
 Private
 
 
@@ -144,7 +152,7 @@ Class FakeParser Implements ICodeParser
 	
 	
 	Method GetConstructors( item:CodeItem,target:Stack<CodeItem> )
 	Method GetConstructors( item:CodeItem,target:Stack<CodeItem> )
 	End
 	End
-	Method ParseFile:String( filePath:String,pathOnDisk:String,moduleName:String )
+	Method ParseFile:String( params:ParseFileParams )
 		'do nothing
 		'do nothing
 		Return Null
 		Return Null
 	End
 	End
@@ -154,13 +162,13 @@ Class FakeParser Implements ICodeParser
 	Method CanShowAutocomplete:Bool( line:String,posInLine:Int )
 	Method CanShowAutocomplete:Bool( line:String,posInLine:Int )
 		Return True
 		Return True
 	End
 	End
-	Method GetScope:CodeItem( docPath:String,docLine:Int )
+	Method GetScope:CodeItem( docPath:String,cursor:Vec2i )
 		Return Null
 		Return Null
 	End
 	End
-	Method GetNearestScope:CodeItem( docPath:String,docLine:Int,above:Bool )
+	Method GetNearestScope:CodeItem( docPath:String,cursor:Vec2i )
 		Return Null
 		Return Null
 	End
 	End
-	Method ItemAtScope:CodeItem( ident:String,filePath:String,docLine:Int )
+	Method ItemAtScope:CodeItem( ident:String,filePath:String,cursor:Vec2i )
 		Return Null
 		Return Null
 	End
 	End
 	Method RefineRawType( item:CodeItem )
 	Method RefineRawType( item:CodeItem )

+ 0 - 3
project.json

@@ -1,3 +0,0 @@
-{
-    "exclude":[".git*", "bin", "*.buildv*", "*.products", "logo"]
-}

+ 47 - 0
testing/ParserTests.monkey2

@@ -1,6 +1,7 @@
 
 
 Namespace test2go
 Namespace test2go
 
 
+Using std..
 
 
 Private
 Private
 
 
@@ -8,18 +9,64 @@ Private
 Class TestTheSame
 Class TestTheSame
 	
 	
 	Property TestTheSame:TestTheSame()
 	Property TestTheSame:TestTheSame()
+		
+		Local abc:="Hello"
+		If abc.Length>5
+			Print "it's longer than 5"
+		Endif
+		abc.Capitalize()
+		Local f7:=""
+		
+		For Local Y:=0 Until 20
+			Local dev:=True
+		Next
+		
 		Return Null
 		Return Null
+		
+	Setter( value:TestTheSame )
+		
+		Local a8:=8
+		
 	End
 	End
 	
 	
 	Method Test( pType:String Ptr )
 	Method Test( pType:String Ptr )
+		
+		For Local Y:=0 Until 20
+			Local dev:=True
+		Next
+		
 		pType->Capitalize()
 		pType->Capitalize()
 		aPtr->Normalize()
 		aPtr->Normalize()
+		
+		Local v:=GetVector()
+		Local c:=New Color
+		Local abc:="Hello"
+		If abc.Length>5
+			Print "it's longer than 5"
+			Local def:=3000
+			
+		Endif
+		
 	End
 	End
 	
 	
 	Field aPtr:Vec2i Ptr
 	Field aPtr:Vec2i Ptr
+	Global ccc:List<Vec3f>
+	
+End
+
+Function LocalTest()
+	
+	Local img:=Image.Load( "" )
+	Local vvv:=GetVector()
 	
 	
 End
 End
 
 
+Function GetVector:Vec2i()
+
+	Return New Vec2i
+End
+
+
 Struct Vec2i Extension
 Struct Vec2i Extension
 	
 	
 	Const One := New Vec2i( 1,1 )
 	Const One := New Vec2i( 1,1 )

+ 11 - 0
utils/TextUtils.monkey2

@@ -1,6 +1,17 @@
 
 
 Namespace ted2go
 Namespace ted2go
 
 
+#Rem monkeydoc Capitalize of numOfChars first chars of the string.
+#End
+Function Capitalize:String( str:String,numOfChars:Int=1 )
+	
+	If numOfChars>=str.Length
+		Return str.ToUpper()
+	Endif
+	
+	Return str.Slice( 0,numOfChars ).ToUpper()+str.Slice( numOfChars )
+End
+
 
 
 Class TextUtils Final
 Class TextUtils Final
 	
 	

+ 7 - 0
utils/Utils.monkey2

@@ -367,6 +367,13 @@ End
 #End
 #End
 Function GetIndentBeforePos_Mx2:IdentInfo( line:String,pos:Int,withDots:Bool )
 Function GetIndentBeforePos_Mx2:IdentInfo( line:String,pos:Int,withDots:Bool )
 	
 	
+	' grab whole word under cursor
+	'
+	Local len:=line.Length
+	While pos < len And IsIdent( line[pos] )
+		pos+=1
+	Wend
+	
 	Local n:=pos-1
 	Local n:=pos-1
 	
 	
 	While n >= 0
 	While n >= 0

+ 5 - 5
view/AutocompleteView.monkey2

@@ -239,13 +239,12 @@ Class AutocompleteDialog Extends NoTitleDialog
 				usings=New Stack<String>
 				usings=New Stack<String>
 				
 				
 				Local locked:=MainWindow.LockedDocument
 				Local locked:=MainWindow.LockedDocument
-				local current:=Cast<CodeDocument>(MainWindow.DocsManager.CurrentDocument)
+				Local current:=Cast<CodeDocument>( MainWindow.DocsManager.CurrentDocument )
 				
 				
 				If Not locked Then locked=current
 				If Not locked Then locked=current
 				If locked
 				If locked
 					Local info:=parser.UsingsMap[locked.Path]
 					Local info:=parser.UsingsMap[locked.Path]
 					If info.nspace Or info.usings
 					If info.nspace Or info.usings
-						usings=New StringStack
 						If info.nspace Then usings.Add( info.nspace+".." )
 						If info.nspace Then usings.Add( info.nspace+".." )
 						If info.usings Then usings.AddAll( info.usings )
 						If info.usings Then usings.AddAll( info.usings )
 					Endif
 					Endif
@@ -255,7 +254,7 @@ Class AutocompleteDialog Extends NoTitleDialog
 					Local info:=parser.UsingsMap[current.Path]
 					Local info:=parser.UsingsMap[current.Path]
 					If info.nspace
 					If info.nspace
 						Local s:=info.nspace+".."
 						Local s:=info.nspace+".."
-						If s And Not usings.Contains( s ) Then usings.Add( s )
+						If Not usings.Contains( s ) Then usings.Add( s )
 					Endif
 					Endif
 					If info.usings Then usings.AddAll( info.usings )
 					If info.usings Then usings.AddAll( info.usings )
 				Endif
 				Endif
@@ -263,14 +262,15 @@ Class AutocompleteDialog Extends NoTitleDialog
 				If Not usings.Contains( "monkey.." ) Then usings.Add( "monkey.." )
 				If Not usings.Contains( "monkey.." ) Then usings.Add( "monkey.." )
 			Endif
 			Endif
 			
 			
+			'Print "usings: "+usings.Join( " " )
+			
 			_listForExtract.Clear()
 			_listForExtract.Clear()
 			
 			
 			Global opts:=New ParserRequestOptions
 			Global opts:=New ParserRequestOptions
 			opts.ident=ident
 			opts.ident=ident
 			opts.filePath=filePath
 			opts.filePath=filePath
-			opts.docLineNum=docLineNum
+			opts.cursor=New Vec2i( docLineNum,docPosInLine )
 			opts.docLineStr=docLineStr
 			opts.docLineStr=docLineStr
-			opts.docPosInLine=docPosInLine
 			opts.results=_listForExtract
 			opts.results=_listForExtract
 			opts.usingsFilter=usings
 			opts.usingsFilter=usings
 			
 			

+ 6 - 0
view/CodeTextView.monkey2

@@ -1407,3 +1407,9 @@ Class IndentationHelper Final
 	End
 	End
 	
 	
 End
 End
+
+Function GetCursorPos:Vec2i( tv:TextView )
+	
+	Local cursor:=tv.Cursor,line:=tv.Document.FindLine( cursor )
+	Return New Vec2i( line+1,cursor-tv.Document.StartOfLine( line ) )
+End

+ 12 - 29
view/CodeTreeView.monkey2

@@ -7,37 +7,23 @@ Class CodeTreeView Extends TreeViewExt
 	Field SortByType:=True
 	Field SortByType:=True
 	Field ShowInherited:=False
 	Field ShowInherited:=False
 	
 	
-	Method Fill( fileType:String,path:String,expandIfOnlyOneItem:Bool=True )
-	
-		Local stack:=New Stack<TreeView.Node>
-		Local parser:=ParsersManager.Get( fileType )
+	Method Fill( codeItems:Stack<CodeItem>,parser:ICodeParser,expandIfOnlyOneItem:Bool=True )
+		
 		Local node:=RootNode
 		Local node:=RootNode
 		
 		
 		RootNodeVisible=False
 		RootNodeVisible=False
 		node.Expanded=True
 		node.Expanded=True
 		node.RemoveAllChildren()
 		node.RemoveAllChildren()
 		
 		
-		_stack.Clear()
-		
-		' extract all items in file
-		Local list:=parser.ItemsMap[path]
-		If list Then _stack.AddAll( list )
-		
-		' extensions are here too
-		For Local lst:=Eachin parser.ExtraItemsMap.Values
-			For Local i:=Eachin lst
-				If i.FilePath=path
-					If Not _stack.Contains( i.Parent ) Then _stack.Add( i.Parent )
-				Endif
-			Next
-		Next
+		If codeItems ' empty stack is correct here - we must remove all
+			_codeItems=codeItems
+		Endif
 		
 		
-		If _stack.Empty Return
+		If Not _codeItems Or _codeItems.Empty Return
 		
 		
-		' sorting
-		SortItems( _stack )
+		SortItems( _codeItems )
 		
 		
-		For Local i:=Eachin _stack
+		For Local i:=Eachin _codeItems
 			AddTreeItem( i,node,parser )
 			AddTreeItem( i,node,parser )
 		Next
 		Next
 		
 		
@@ -50,6 +36,7 @@ Class CodeTreeView Extends TreeViewExt
 	Method SelectByScope( scope:CodeItem )
 	Method SelectByScope( scope:CodeItem )
 		
 		
 		Local node:=FindNode( RootNode,scope )
 		Local node:=FindNode( RootNode,scope )
+		If Not node And scope.Parent Then node=FindNode( RootNode,scope.Parent )
 		If Not node Return
 		If Not node Return
 		
 		
 		node.Expanded=True
 		node.Expanded=True
@@ -64,7 +51,7 @@ Class CodeTreeView Extends TreeViewExt
 	
 	
 	Private
 	Private
 	
 	
-	Field _stack:=New Stack<CodeItem>
+	Field _codeItems:Stack<CodeItem>
 	
 	
 	Method FindNode:TreeView.Node( treeNode:TreeView.Node,item:CodeItem )
 	Method FindNode:TreeView.Node( treeNode:TreeView.Node,item:CodeItem )
 	
 	
@@ -100,7 +87,7 @@ Class CodeTreeView Extends TreeViewExt
 		
 		
 		' sorting only root class members
 		' sorting only root class members
 		If item.IsLikeClass
 		If item.IsLikeClass
-				
+			
 			SortItems( list )
 			SortItems( list )
 			
 			
 			If ShowInherited
 			If ShowInherited
@@ -111,10 +98,6 @@ Class CodeTreeView Extends TreeViewExt
 					inherRoot.Children=lst
 					inherRoot.Children=lst
 					inherRoot.KindStr="inherited"
 					inherRoot.KindStr="inherited"
 					list.Insert( 0,inherRoot )
 					list.Insert( 0,inherRoot )
-					'For Local i:=Eachin lst
-					'	Local children:=i.Children
-					'	
-					'Next
 				Endif
 				Endif
 			Endif
 			Endif
 		End
 		End
@@ -123,7 +106,7 @@ Class CodeTreeView Extends TreeViewExt
 		
 		
 		Local added:=New StringStack
 		Local added:=New StringStack
 		For Local i:=Eachin list
 		For Local i:=Eachin list
-			If i.Kind = CodeItemKind.Param_ Continue
+			If i.KindStr="block" Continue
 			Local txt:=i.Text
 			Local txt:=i.Text
 			If added.Contains( txt ) Continue
 			If added.Contains( txt ) Continue
 			added.Add( txt )
 			added.Add( txt )

+ 62 - 55
view/HelpTreeView.monkey2

@@ -57,8 +57,6 @@ Class HelpTreeView Extends TreeViewExt
 			MainWindow.ShowHelp( url )
 			MainWindow.ShowHelp( url )
 		End
 		End
 		
 		
-		Init()
-		
 	End
 	End
 	
 	
 	Property FindField:TextFieldExt()
 	Property FindField:TextFieldExt()
@@ -148,11 +146,17 @@ Class HelpTreeView Extends TreeViewExt
 			
 			
 			'new doc system
 			'new doc system
 			Local index:="docs/modules/"+modname+"/module/index.js"
 			Local index:="docs/modules/"+modname+"/module/index.js"
+			Try
 			
 			
-			Local obj:=JsonObject.Load( index )
-			If Not obj Continue
-			
-			CreateNodes( obj,_tree.RootNode,_index )
+				Local obj:=JsonObject.Load( index,True )
+				If Not obj Continue
+				
+				CreateNodes( obj,_tree.RootNode,_index )
+				
+			Catch ex:Throwable
+				
+				Print "Can't parse doc file: "+index
+			End
 		Next
 		Next
 		
 		
 		FillTree()
 		FillTree()
@@ -165,9 +169,57 @@ Class HelpTreeView Extends TreeViewExt
 		_textField.MakeKeyView()
 		_textField.MakeKeyView()
 	End
 	End
 	
 	
+	Method Init()
+		
+		_textField=New TextFieldExt( "" )
+		_textField.Style=GetStyle( "HelpTextField" )
+		
+		_textField.Entered=Lambda()
+			
+			NextHelp()
+		End
+		
+		_textField.Document.TextChanged=Lambda()
+			
+			Local text:=_textField.Text
+			
+			Update( text )
+		End
+		
+		Local find:=New Label( "Find " )
+		find.AddView( _textField )
+		
+		AddView( find,"top" )
+		
+		RootNodeVisible=False
+		RootNode.Expanded=True
+		
+		NodeClicked+=Lambda( tnode:TreeView.Node )
+			
+			Local node:=Cast<Node>( tnode )
+			Local page:=node?.Page
+			If Not page Return
+			
+			If page="$$rebuild$$"
+				MainWindow.RebuildDocs()
+				Return
+			Endif
+			
+			PageClicked( page )
+		End
+		
+		Update()
+	End
 	
 	
 	Private
 	Private
 	
 	
+	Field _textField:TextFieldExt
+	Field _matchid:Int
+	Field _matches:=New Stack<Node>
+	Field _index:=New Map<String,Tree.Node>
+	Field _index2:=New Map<String,Node>
+	Field _tree:=New Tree
+	
 	Class Node Extends TreeView.Node
 	Class Node Extends TreeView.Node
 	
 	
 		Method New( text:String,parent:TreeView.Node,page:String )
 		Method New( text:String,parent:TreeView.Node,page:String )
@@ -194,6 +246,10 @@ Class HelpTreeView Extends TreeViewExt
 		FillNode( RootNode,_tree.RootNode.Children )
 		FillNode( RootNode,_tree.RootNode.Children )
 		
 		
 		Sort()
 		Sort()
+		
+		If RootNode.Children.Length=0
+			New Node( "No docs found; you can use 'Help -- Rebuild docs'.",RootNode,"" )
+		Endif
 	End
 	End
 	
 	
 	Method FillNode( node:TreeView.Node,items:Stack<Tree.Node> )
 	Method FillNode( node:TreeView.Node,items:Stack<Tree.Node> )
@@ -313,53 +369,4 @@ Class HelpTreeView Extends TreeViewExt
 		Selected=_matches[_matchid]
 		Selected=_matches[_matchid]
 	End
 	End
 	
 	
-	Method Init()
-		
-		_textField=New TextFieldExt( "" )
-		_textField.Style=GetStyle( "HelpTextField" )
-		
-		_textField.Entered=Lambda()
-		
-			NextHelp()
-		End
-		
-		_textField.Document.TextChanged=Lambda()
-		
-			Local text:=_textField.Text
-			
-			Update( text )
-		End
-		
-		Local find:=New Label( "Find " )
-		find.AddView( _textField )
-		
-		AddView( find,"top" )
-	
-		RootNodeVisible=False
-		RootNode.Expanded=True
-		
-		NodeClicked+=Lambda( tnode:TreeView.Node )
-		
-			Local node:=Cast<Node>( tnode )
-			Local page:=node?.Page
-			If Not page Return
-			
-			If page="$$rebuild$$"
-				MainWindow.RebuildDocs()
-				Return
-			Endif
-			
-			PageClicked( page )
-		End
-		
-		Update()
-	End
-	
-	Field _textField:TextFieldExt
-	Field _matchid:Int
-	Field _matches:=New Stack<Node>
-	Field _index:=New Map<String,Tree.Node>
-	Field _index2:=New Map<String,Node>
-	Field _tree:=New Tree
-	
 End
 End

+ 4 - 2
view/HtmlViewExt.monkey2

@@ -7,9 +7,9 @@ Class HtmlViewExt Extends HtmlView
 	Field Navigated:Void( url:String )
 	Field Navigated:Void( url:String )
 	
 	
 	Method New()
 	Method New()
-	
+		
 		Super.New()
 		Super.New()
-				
+		
 		_navOps.OnNavigate += Lambda( nav:Nav )
 		_navOps.OnNavigate += Lambda( nav:Nav )
 			
 			
 			Go( nav.url )
 			Go( nav.url )
@@ -66,6 +66,8 @@ Class HtmlViewExt Extends HtmlView
 	
 	
 	Method StoreScroll()
 	Method StoreScroll()
 		
 		
+		If Not _navOps.Empty Return
+		
 		Local nav:=_navOps.Current
 		Local nav:=_navOps.Current
 		If nav Then nav.scroll=Scroll
 		If nav Then nav.scroll=Scroll
 	End
 	End

+ 4 - 3
view/Monkey2TreeView.monkey2

@@ -44,9 +44,10 @@ Class Monkey2TreeView Extends JsonTreeView
 	End
 	End
 	
 	
 	Method UpdateTree( path:String )
 	Method UpdateTree( path:String )
-	
-		Local cmd:="~q"+MainWindow.Mx2ccPath+"~q makeapp -parse -geninfo ~q"+path+"~q"
-					
+		
+		Local cmd:=Monkey2Parser.GetParseCommand( path )
+		If Not cmd Return
+		
 		Local str:=LoadString( "process::"+cmd )
 		Local str:=LoadString( "process::"+cmd )
 		
 		
 		Local jobj:JsonObject,i:=str.Find( "{" )
 		Local jobj:JsonObject,i:=str.Find( "{" )

+ 94 - 18
view/ProjectBrowserView.monkey2

@@ -206,25 +206,76 @@ Class ProjectBrowserView Extends TreeViewExt Implements IDraggableHolder
 		Return New Node( parent,Self )
 		Return New Node( parent,Self )
 	End
 	End
 	
 	
-	Method AddProject( dir:String )
+	Method SetActiveProject( project:Monkey2Project )
+	
+		For Local n:=Eachin RootNode.Children
+			Local node:=Cast<Node>( n )
+			Local s:=PrepareProjectName( node._project )
+			If node._project=project Then s="+"+s
+			node.Text=s
+		Next
+	End
+	
+	Method SetMainFile( path:String,set:Bool )
+		
+		If Not Prefs.MainProjectIcons Return
 		
 		
+		Local node:=_rootNode.FindNodeByPath( path )
+		If node
+			node.Icon=set ? ThemeImages.Get( "project/monkey2main.png" ) Else GetFileTypeIcon( node._path )
+		Endif
+	End
+	
+	Method AddProject( project:Monkey2Project )
+	
 		Local node:=NewNode( _rootNode )
 		Local node:=NewNode( _rootNode )
-		Local s:=StripDir( dir )+" ("+dir+")"
+		Local s:=PrepareProjectName( project )
 		node.Text=s
 		node.Text=s
-		node._path=dir
+		node._path=project.Folder
+		node._project=project
 		UpdateProjIcon( node )
 		UpdateProjIcon( node )
 		_expander.Restore( node )
 		_expander.Restore( node )
-		
+	
 		UpdateNode( node )
 		UpdateNode( node )
 		ApplyFilter( node )
 		ApplyFilter( node )
+		
+		Local mainFile:=project.MainFilePath
+		If mainFile Then SetMainFile( mainFile,True )
+		
 	End
 	End
 	
 	
-	Method RemoveProject( dir:String )
+	Method RefreshProject( project:Monkey2Project )
+		
+		Local folder:=project.Folder
+		Local projNode:Node=Null
+		For Local n:=Eachin RootNode.Children
+			Local node:=Cast<Node>( n )
+			If node._project.Folder=folder
+				projNode=node
+				Exit
+			Endif
+		Next
+		
+		If Not projNode Return
+		
+		projNode._project=project
+		UpdateProjIcon( projNode )
+		_expander.Restore( projNode )
+	
+		UpdateNode( projNode )
+		ApplyFilter( projNode )
+	
+		Local mainFile:=project.MainFilePath
+		If mainFile Then SetMainFile( mainFile,True )
+		
+	End
+	
+	Method RemoveProject( project:Monkey2Project )
 	
 	
-		Local s:=StripDir( dir )+" ("+dir+")"
 		Local toRemove:TreeView.Node=Null
 		Local toRemove:TreeView.Node=Null
 		For Local n:=Eachin RootNode.Children
 		For Local n:=Eachin RootNode.Children
-			If n.Text=s
+			Local node:=Cast<Node>( n )
+			If node._project=project
 				toRemove=n
 				toRemove=n
 				Exit
 				Exit
 			Endif
 			Endif
@@ -293,6 +344,10 @@ Class ProjectBrowserView Extends TreeViewExt Implements IDraggableHolder
 			Return _path
 			Return _path
 		End
 		End
 		
 		
+		Property Project:Monkey2Project()
+			Return _project
+		End
+		
 		Property Detachable:Bool()
 		Property Detachable:Bool()
 			Return GetNodeDeepLevel( Self )>1
 			Return GetNodeDeepLevel( Self )>1
 		End
 		End
@@ -316,12 +371,30 @@ Class ProjectBrowserView Extends TreeViewExt Implements IDraggableHolder
 			_view=view
 			_view=view
 		End
 		End
 		
 		
+		Method FindNodeByPath:Node( path:String )
+			
+			If Children
+				For Local i:=Eachin Children
+					Local n:=Cast<Node>( i )
+					If n._path=path Return n
+					If n.Children
+						n=n.FindNodeByPath( path )
+						If n Return n
+					Endif
+				Next
+			Endif
+			
+			Return Null
+		End
+		
+		
 		Private
 		Private
-	
+		
 		Field _path:String
 		Field _path:String
 		Field _holders:ProjectBrowserView[]
 		Field _holders:ProjectBrowserView[]
 		Field _curHolder:ProjectBrowserView
 		Field _curHolder:ProjectBrowserView
 		Field _view:View
 		Field _view:View
+		Field _project:Monkey2Project
 		
 		
 	End
 	End
 	
 	
@@ -408,6 +481,12 @@ Class ProjectBrowserView Extends TreeViewExt Implements IDraggableHolder
 		Return True
 		Return True
 	End
 	End
 	
 	
+	Method PrepareProjectName:String( project:Monkey2Project )
+		
+		Local dir:=project.Folder
+		Return StripDir( dir )+" ("+dir+")"
+	End
+	
 	Method OnDraggedInto( node:Node,name:String )
 	Method OnDraggedInto( node:Node,name:String )
 		
 		
 		Local path:=GetNodePath( node )+"\"+name
 		Local path:=GetNodePath( node )+"\"+name
@@ -494,12 +573,12 @@ Class ProjectBrowserView Extends TreeViewExt Implements IDraggableHolder
 	
 	
 	Method UpdateFilterItems( projNode:Node )
 	Method UpdateFilterItems( projNode:Node )
 		
 		
-		Local path:=projNode.Path+"/project.json"
-		If GetFileType( path ) <> FileType.File Return
+		Local proj:=projNode._project
+		If proj.IsFolderBased Return
 		
 		
 		Local projName:=projNode.Text
 		Local projName:=projNode.Text
 		
 		
-		Local t:=GetFileTime( path )
+		Local t:=proj.Modified
 		If t=_filtersFileTimes[projName] Return
 		If t=_filtersFileTimes[projName] Return
 		
 		
 		_filtersFileTimes[projName]=t
 		_filtersFileTimes[projName]=t
@@ -507,13 +586,10 @@ Class ProjectBrowserView Extends TreeViewExt Implements IDraggableHolder
 		Local list:=GetOrCreate( _filters,projName )
 		Local list:=GetOrCreate( _filters,projName )
 		list.Clear()
 		list.Clear()
 		
 		
-		Local json:=JsonObject.Load( path )
-		If json.Contains( "exclude" )
-			For Local i:=Eachin json["exclude"].ToArray()
-				Local f:=New TextFilter( i.ToString() )
-				list+=f
-			Next
-		Endif
+		For Local i:=Eachin proj.Excluded
+			Local f:=New TextFilter( i )
+			list+=f
+		Next
 	End
 	End
 	
 	
 	Method UpdateProjIcon( node:TreeView.Node )
 	Method UpdateProjIcon( node:TreeView.Node )

+ 369 - 48
view/ProjectView.monkey2

@@ -4,13 +4,17 @@ Namespace ted2go
 
 
 Class ProjectView Extends DockingView
 Class ProjectView Extends DockingView
 
 
-	Field openProject:Action
+	Field openProjectFolder:Action
+	Field openProjectFile:Action
+	Field setMainFile:Action
 	
 	
-	Field ProjectOpened:Void( dir:String )
-	Field ProjectClosed:Void( dir:String )
+	Field ProjectOpened:Void( path:String )
+	Field ProjectClosed:Void( path:String )
+	Field ActiveProjectChanged:Void( proj:Monkey2Project )
 	
 	
 	Field RequestedFindInFolder:Void( folder:String )
 	Field RequestedFindInFolder:Void( folder:String )
-
+	Field MainFileChanged:Void( path:String,prevPath:String )
+	
 	Method New( docs:DocumentManager,builder:IModuleBuilder )
 	Method New( docs:DocumentManager,builder:IModuleBuilder )
 	
 	
 		_docs=docs
 		_docs=docs
@@ -20,12 +24,50 @@ Class ProjectView Extends DockingView
 		
 		
 		ContentView=_docker
 		ContentView=_docker
 		
 		
-		openProject=New Action( "Open project" )
-		openProject.HotKey=Key.O
-		openProject.HotKeyModifiers=Modifier.Menu|Modifier.Shift
-		openProject.Triggered=OnOpenProject
+		openProjectFolder=New Action( "Open project folder" )
+		openProjectFolder.HotKey=Key.O
+		openProjectFolder.HotKeyModifiers=Modifier.Menu|Modifier.Shift
+		openProjectFolder.Triggered=OnOpenProjectFolder
+		
+		openProjectFile=New Action( "Open project file" )
+		'openProjectFile.HotKey=Key.O
+		'openProjectFile.HotKeyModifiers=Modifier.Menu|Modifier.Shift
+		openProjectFile.Triggered=OnOpenProjectFile
+		
+		setMainFile=New Action( "Set as main file" )
+		setMainFile.Triggered=Lambda()
+			
+			Local doc:=_docs.CurrentCodeDocument
+			If doc Then SetMainFile( doc.Path )
+		End
 		
 		
 		InitProjBrowser()
 		InitProjBrowser()
+		
+'		_docs.LockedDocumentChanged+=Lambda:Void()
+'			Local path:=_docs.LockedDocument?.Path
+'			If path Then SetActiveProject( path )
+'		End
+		
+		App.Activated+=Lambda()
+			
+			For Local proj:=Eachin _projects
+				Local changed:=proj.Reload()
+				If changed
+					_projBrowser?.RefreshProject( proj )
+				Endif
+			Next
+		End
+		
+		ActiveProjectChanged+=Lambda( proj:Monkey2Project )
+		
+			_projBrowser?.SetActiveProject( proj )
+		End
+		
+		MainFileChanged+=Lambda( path:String,prevPath:String )
+			
+			_projBrowser?.SetMainFile( prevPath,False )
+			_projBrowser?.SetMainFile( path,True )
+		End
 	End
 	End
 	
 	
 	Property SelectedItem:ProjectBrowserView.Node()
 	Property SelectedItem:ProjectBrowserView.Node()
@@ -33,9 +75,24 @@ Class ProjectView Extends DockingView
 		Return Cast<ProjectBrowserView.Node>( _projBrowser.Selected )
 		Return Cast<ProjectBrowserView.Node>( _projBrowser.Selected )
 	End
 	End
 	
 	
-	Property OpenProjects:String[]()
+	Property OpenProjects:Stack<Monkey2Project>()
+		
+		Return _projects
+	End
 	
 	
-		Return _projects.ToArray()
+	Property OpenProjectsFolders:String[]()
+	
+		Local folders:=New String[_projects.Length]
+		For Local i:=0 Until _projects.Length
+			folders[i]=_projects[i].Folder
+		Next
+	
+		Return folders
+	End
+	
+	Property ActiveProject:Monkey2Project()
+	
+		Return _activeProject
 	End
 	End
 	
 	
 	Property SingleClickExpanding:Bool()
 	Property SingleClickExpanding:Bool()
@@ -47,24 +104,47 @@ Class ProjectView Extends DockingView
 		_projBrowser.SingleClickExpanding=value
 		_projBrowser.SingleClickExpanding=value
 	End
 	End
 	
 	
-	Function FindProjectByFile:String( filePath:String )
-		
-		If Not filePath Return ""
+	Function FindProject:Monkey2Project( filePath:String )
+	
+		If Not filePath Return Null
 		
 		
-		For Local p:=Eachin _projects
-			If filePath.StartsWith( p )
-				Return p
+		For Local proj:=Eachin _projects
+			If filePath.StartsWith( proj.Folder )
+				Return proj
 			Endif
 			Endif
-		End
-		Return ""
+		Next
+		
+		Return Null
+	End
+	
+	Function IsProjectFile:Bool( filePath:String )
+	
+		Return ExtractExt( filePath )=".mx2proj"
+	End
+	
+	Function IsValidProject:Bool( path:String )
+	
+		Return IsProjectFile( path ) Or GetFileType( path )=FileType.Directory
+	End
+	
+	Function ActiveProjectName:String()
+	
+		Return _activeProject?.Name
+	End
+	
+	Function CheckMainFilePath( proj:Monkey2Project )
+		
+		If Not proj.MainFilePath
+			Alert( "Main file of ~q"+proj.Name+"~q project is not specified.~n~nRight click on file in Project tree~nand choose 'Set as main file'.","Build error" )
+		Endif
+	
 	End
 	End
 	
 	
 	Method OnFileDropped:Bool( path:String )
 	Method OnFileDropped:Bool( path:String )
 		
 		
 		Local ok:=_projBrowser.OnFileDropped( path )
 		Local ok:=_projBrowser.OnFileDropped( path )
 		If Not ok
 		If Not ok
-			Local isFolder:=GetFileType( path )=FileType.Directory
-			If isFolder
+			If IsValidProject( path )
 				ok=True
 				ok=True
 				OpenProject( path )
 				OpenProject( path )
 			Endif
 			Endif
@@ -72,30 +152,68 @@ Class ProjectView Extends DockingView
 		Return ok
 		Return ok
 	End
 	End
 	
 	
-	Method OpenProject:Bool( dir:String )
+	Method SetActiveProject( path:String,prompt:Bool=True )
+		
+		Local proj:=FindProject( path )
+		If proj
+			If proj.IsFolderBased
+				If Not prompt Return
+				proj=ShowCreateProjectFilePrompt( "Can't set folder-based project as active.",proj )
+				If Not proj Return
+			Endif
+			OnActiveProjectChanged( proj )
+		Endif
+	End
+	
+	Method SetMainFile( path:String,prompt:Bool=True )
 	
 	
-		dir=StripSlashes( dir )
+		Local proj:=FindProject( path )
+		If proj
+			If proj.IsFolderBased
+				If Not prompt Return
+				proj=ShowCreateProjectFilePrompt( "Can't set main file of folder-based project.",proj )
+				If Not proj Return
+			Endif
+			Local prev:=proj.MainFilePath
+			proj.MainFilePath=path
+			MainFileChanged( path,prev )
+		Endif
+	End
+	
+	Method OpenProject:Bool( path:String )
 		
 		
-		If _projects.Contains( dir ) Return False
+		Local proj:=FindProject( path )
+		Local isProjExists:=(proj<>Null)
 		
 		
-		If GetFileType( dir )<>FileType.Directory Return False
+		Local projIndex:=_projects.FindIndex( proj )
+		Local wasActive:=(proj=_activeProject)
 		
 		
-		_projects+=dir
+		proj=New Monkey2Project( path )
 		
 		
-		_projBrowser.AddProject( dir )
+		If isProjExists
+			_projects.Set( projIndex,proj )
+			_projBrowser.RefreshProject( proj )
+			If wasActive Then OnActiveProjectChanged( proj )
+		Else
+			_projects+=proj
+			_projBrowser.AddProject( proj )
+		Endif
+		
+		ProjectOpened( proj.Path )
 		
 		
-		ProjectOpened( dir )
-
 		Return True
 		Return True
 	End
 	End
 	
 	
 	Method CloseProject( dir:String )
 	Method CloseProject( dir:String )
-
-		dir=StripSlashes( dir )
 		
 		
-		_projBrowser.RemoveProject( dir )
+		Local proj:=FindProject( dir )
+		If Not proj Return
+		
+		_projBrowser.RemoveProject( proj )
 		
 		
-		_projects-=dir
+		_projects-=proj
+		
+		If proj=_activeProject Then OnActiveProjectChanged( Null )
 		
 		
 		ProjectClosed( dir )
 		ProjectClosed( dir )
 	End
 	End
@@ -107,7 +225,7 @@ Class ProjectView Extends DockingView
 		
 		
 		Local jarr:=New JsonArray
 		Local jarr:=New JsonArray
 		For Local p:=Eachin _projects
 		For Local p:=Eachin _projects
-			jarr.Add( New JsonString( p ) )
+			jarr.Add( New JsonString( p.Path ) )
 		Next
 		Next
 		j["openProjects"]=jarr
 		j["openProjects"]=jarr
 		
 		
@@ -115,6 +233,13 @@ Class ProjectView Extends DockingView
 		
 		
 		Local selPath:=GetNodePath( _projBrowser.Selected )
 		Local selPath:=GetNodePath( _projBrowser.Selected )
 		j["selected"]=New JsonString( selPath )
 		j["selected"]=New JsonString( selPath )
+		
+		If _activeProject Then j["active"]=New JsonString( _activeProject.Path )
+	End
+	
+	Property HasOpenedProjects:Bool()
+		
+		Return _projects.Length>0
 	End
 	End
 	
 	
 	Method LoadState( jobj:JsonObject )
 	Method LoadState( jobj:JsonObject )
@@ -126,14 +251,20 @@ Class ProjectView Extends DockingView
 		_projBrowser.LoadState( jobj,"expanded" )
 		_projBrowser.LoadState( jobj,"expanded" )
 		
 		
 		If jobj.Contains( "openProjects" )
 		If jobj.Contains( "openProjects" )
-			local arr:=jobj["openProjects"].ToArray()
-			For Local dir:=Eachin arr
-				OpenProject( dir.ToString() )
+			Local arr:=jobj["openProjects"].ToArray()
+			For Local path:=Eachin arr
+				OpenProject( path.ToString() )
 			Next
 			Next
+			If arr.Length=1
+				jobj["active"]=New JsonString( _projects[0].Path )
+			Endif
 		Endif
 		Endif
 		
 		
 		Local selPath:=Json_GetString( jobj.Data,"selected","" )
 		Local selPath:=Json_GetString( jobj.Data,"selected","" )
 		If selPath Then _projBrowser.SelectByPath( selPath )
 		If selPath Then _projBrowser.SelectByPath( selPath )
+		
+		Local activePath:=Json_GetString( jobj.Data,"active","" )
+		If activePath Then SetActiveProject( activePath,False )
 	End
 	End
 	
 	
 	
 	
@@ -144,12 +275,47 @@ Class ProjectView Extends DockingView
 	
 	
 	Field _docs:DocumentManager
 	Field _docs:DocumentManager
 	Field _docker:=New DockingView
 	Field _docker:=New DockingView
-	Global _projects:=New StringStack
+	'Global _projectFolders:=New StringStack
+	Global _projects:=New Stack<Monkey2Project>
 	Field _builder:IModuleBuilder
 	Field _builder:IModuleBuilder
 	Field _projBrowser:ProjectBrowserView
 	Field _projBrowser:ProjectBrowserView
-	
+	Global _activeProject:Monkey2Project
 	Field _cutPath:String,_copyPath:String
 	Field _cutPath:String,_copyPath:String
 	
 	
+	Method ShowCreateProjectFilePrompt:Monkey2Project( prompt:String,proj:Monkey2Project )
+		
+		Local yes:=RequestOkay( prompt+"~n~nDo you want create project file for the project?","Projects","Yes","No" )
+		If Not yes Return Null
+		
+		Local path:String
+		Repeat 
+			Local name:=RequestString( "Project filename:","Projects",StripDir( proj.Folder ) ).Trim()
+			If Not name
+				Alert( "Name wasn't entered, so do nothing.","Projects" )
+				Return Null
+			Endif
+			If ExtractExt( name )<>".mx2proj"
+				name+=".mx2proj"
+			Endif
+			path=proj.Folder+"/"+name
+			
+			If FileExists( path )
+				Local yes:=RequestOkay( "Such project file already exists.~nDo you want use it for the project?","Projects","Use it","Create another" )
+				If Not yes Continue
+			Else
+				' don't overwrite existing files
+				Monkey2Project.SaveEmptyProject( path )
+			Endif
+			
+			Exit
+			
+		Forever
+		
+		OpenProject( path )
+		
+		Return FindProject( path )
+	End
+	
 	Method OnCut( path:String )
 	Method OnCut( path:String )
 		
 		
 		_copyPath=""
 		_copyPath=""
@@ -225,7 +391,7 @@ Class ProjectView Extends DockingView
 				
 				
 			Else
 			Else
 				
 				
-				If Not RequestOkay( "Really delete file '"+path+"'?" ) Print "1111" ; Return
+				If Not RequestOkay( "Really delete file '"+path+"'?" ) Return
 				
 				
 				If DeleteFile( path )
 				If DeleteFile( path )
 				
 				
@@ -246,18 +412,40 @@ Class ProjectView Extends DockingView
 		New Fiber( work )
 		New Fiber( work )
 	End
 	End
 	
 	
-	Method OnOpenProject()
+	Method OnOpenProjectFolder()
 	
 	
-		Local dir:=MainWindow.RequestDir( "Select Project Directory...","" )
+		Local dir:=MainWindow.RequestDir( "Select project folder...","" )
 		If Not dir Return
 		If Not dir Return
-		
+	
 		OpenProject( dir )
 		OpenProject( dir )
+		
+'		If _projects.Length=1
+'			OnActiveProjectChanged( _projects[0] )
+'		Endif
+	End
+	
+	Method OnActiveProjectChanged( proj:Monkey2Project )
+		
+		_activeProject=proj
+		ActiveProjectChanged( _activeProject )
+	End
+	
+	Method OnOpenProjectFile()
+	
+		Local file:=MainWindow.RequestFile( "Select project file...","",False,"Monkey2 projects:mx2proj" )
+		If Not file Return
+	
+		OpenProject( file )
+		
+		If _projects.Length=1
+			OnActiveProjectChanged( _projects[0] )
+		Endif
 	End
 	End
 	
 	
 	Method OnOpenDocument( path:String,makeFocused:Bool,runExec:Bool=True )
 	Method OnOpenDocument( path:String,makeFocused:Bool,runExec:Bool=True )
 		
 		
 		If GetFileType( path )<>FileType.File Return
 		If GetFileType( path )<>FileType.File Return
-			
+		
 		New Fiber( Lambda()
 		New Fiber( Lambda()
 			
 			
 			Local ext:=ExtractExt( path )
 			Local ext:=ExtractExt( path )
@@ -330,7 +518,7 @@ Class ProjectView Extends DockingView
 		_docker.ContentView=browser
 		_docker.ContentView=browser
 		
 		
 		browser.RequestedDelete+=Lambda( node:ProjectBrowserView.Node )
 		browser.RequestedDelete+=Lambda( node:ProjectBrowserView.Node )
-		
+			
 			DeleteItem( browser,node.Path,node )
 			DeleteItem( browser,node.Path,node )
 		End
 		End
 		
 		
@@ -345,7 +533,7 @@ Class ProjectView Extends DockingView
 		End
 		End
 		
 		
 		browser.FileRightClicked+=Lambda( node:ProjectBrowserView.Node )
 		browser.FileRightClicked+=Lambda( node:ProjectBrowserView.Node )
-		
+			
 			Local menu:=New MenuExt
 			Local menu:=New MenuExt
 			Local path:=node.Path
 			Local path:=node.Path
 			Local pasteAction:Action
 			Local pasteAction:Action
@@ -353,12 +541,12 @@ Class ProjectView Extends DockingView
 			Local fileType:=GetFileType( path )
 			Local fileType:=GetFileType( path )
 			
 			
 			menu.AddAction( "Open on Desktop" ).Triggered=Lambda()
 			menu.AddAction( "Open on Desktop" ).Triggered=Lambda()
-			
+				
 				Local p:=(fileType=FileType.File) ? ExtractDir( path ) Else path
 				Local p:=(fileType=FileType.File) ? ExtractDir( path ) Else path
 				requesters.OpenUrl( p )
 				requesters.OpenUrl( p )
 			End
 			End
 			menu.AddAction( "Copy path" ).Triggered=Lambda()
 			menu.AddAction( "Copy path" ).Triggered=Lambda()
-			
+				
 				App.ClipboardText=path
 				App.ClipboardText=path
 			End
 			End
 			
 			
@@ -460,8 +648,22 @@ Class ProjectView Extends DockingView
 				
 				
 				If browser.IsProjectNode( node ) ' root node
 				If browser.IsProjectNode( node ) ' root node
 					
 					
+					menu.AddAction( "Build & Run" ).Triggered=Lambda()
+						PathsProvider.SetCustomBuildProject( node.Project )
+						Local buildActions:=Di.Resolve<BuildActions>()
+						buildActions.buildAndRun.Triggered()
+						PathsProvider.SetCustomBuildProject( Null )
+					End
+					
+					menu.AddAction( "Set as active project" ).Triggered=Lambda()
+					
+						SetActiveProject( path )
+					End
+					
 					menu.AddAction( "Close project" ).Triggered=Lambda()
 					menu.AddAction( "Close project" ).Triggered=Lambda()
-						
+					
+						If Not RequestOkay( "Really close project?" ) Return
+					
 						CloseProject( path )
 						CloseProject( path )
 					End
 					End
 					
 					
@@ -529,6 +731,12 @@ Class ProjectView Extends DockingView
 			
 			
 			Case FileType.File
 			Case FileType.File
 				
 				
+				menu.AddAction( "Set as main file" ).Triggered=Lambda()
+				
+					SetMainFile( path )
+				End
+				menu.AddSeparator()
+				
 				menu.AddAction( "Rename file" ).Triggered=Lambda()
 				menu.AddAction( "Rename file" ).Triggered=Lambda()
 					
 					
 					Local oldName:=StripDir( path )
 					Local oldName:=StripDir( path )
@@ -609,3 +817,116 @@ Class ProjectView Extends DockingView
 	End
 	End
 	
 	
 End
 End
+
+
+Class Monkey2Project
+	
+	Const KEY_MAIN_FILE:="mainFile"
+	Const KEY_HIDDEN:="hidden"
+	
+	Function SaveEmptyProject( path:String )
+		
+		Local jobj:=New JsonObject
+		jobj[KEY_MAIN_FILE]=New JsonString
+		jobj[KEY_HIDDEN]=New JsonArray
+		
+		SaveString( jobj.ToJson(),path )
+	End
+	
+	Method New( path:String )
+		
+		_path=path
+		
+		If GetFileType( path )=FileType.File
+			_data=JsonObject.Load( path )
+			_modified=GetFileTime( path )
+			path=ExtractDir( path )
+		Else
+			_data=New JsonObject
+			_isFolderBased=True
+		Endif
+		
+		_folder=StripSlashes( path )
+	End
+	
+	Property MainFile:String()
+		Return _data.GetString( KEY_MAIN_FILE )
+	End
+	
+	Property MainFilePath:String()
+		Local main:=MainFile
+		Return main ? Folder+"/"+main Else ""
+	Setter( value:String )
+		_data.SetString( KEY_MAIN_FILE,value.Replace(Folder+"/","" ) )
+		OnChanged()
+	End
+	
+	Property Folder:String()
+		Return _folder
+	End
+	
+	Property Name:String()
+		Return StripDir( _folder )
+	End
+	
+	Property IsFolderBased:Bool()
+		Return _isFolderBased
+	End
+	
+	Property Path:String()
+		Return _path
+	End
+	
+	Property Modified:Int()
+		Return _modified
+	End
+	
+	Property Excluded:String[]()
+		
+		If _modified=0 Return New String[0]
+		If _modified=_excludedTime Return _excluded
+		
+		Local jarr:=_data.GetArray( KEY_HIDDEN )
+		If Not jarr Or jarr.Empty Return New String[0]
+		
+		_excluded=New String[jarr.Length]
+		For Local i:=0 Until jarr.Length
+			_excluded[i]=jarr[i].ToString()
+		Next
+		
+		Return _excluded
+	End
+	
+	Method Save()
+		
+		If Not _isFolderBased Then SaveString( _data.ToJson(),_path )
+	End
+	
+	Method Reload:Bool()
+	
+		If _isFolderBased Return False
+		
+		Local t:=GetFileTime( _path )
+		If t>_modified
+			_data=JsonObject.Load( _path )
+			_modified=t
+			Return True
+		Endif
+		
+		Return False
+	End
+	
+	Private
+	
+	Field _path:String,_folder:String
+	Field _data:JsonObject
+	Field _isFolderBased:Bool
+	Field _modified:Int
+	Field _excluded:String[],_excludedTime:Int
+	
+	Method OnChanged()
+		
+		Save()
+	End
+	
+End