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*
 *.products/
 *.app
-*.DS_*
+*.DS_*
+*.mx2/

+ 82 - 62
MainWindow.monkey2

@@ -30,16 +30,16 @@ Class MainWindowInstance Extends Window
 		
 		_tabsWrap=New DraggableTabs
 		
-		_docsTabView=New TabViewExt( TabViewFlags.DraggableTabs|TabViewFlags.ClosableTabs )
+		_docsTabView=Di.Resolve<DocsTabView>()
 		
 		_recentFilesMenu=New MenuExt( "Recent files" )
 		_recentProjectsMenu=New MenuExt( "Recent projects" )
 		_closeProjectMenu=New MenuExt( "Close project" )
 		
-		_docBrowser=New DockingView
+		_docBrowser=Di.Resolve<DocBrowserView>()
+		
+		_docsManager=Di.Resolve<DocumentManager>()
 		
-		_docsManager=New DocumentManager( _docsTabView,_docBrowser )
-
 		_docsManager.CurrentDocumentChanged+=Lambda()
 			
 			UpdateKeyView()
@@ -59,7 +59,7 @@ Class MainWindowInstance Extends Window
 		
 		_docsManager.DocumentDoubleClicked+=Lambda( doc:Ted2Document )
 		
-			_buildActions.LockBuildFile()
+			_docsManager.LockBuildFile()
 		End
 		
 		App.FileDropped+=Lambda( path:String )
@@ -68,24 +68,32 @@ Class MainWindowInstance Extends Window
 		End
 		
 		_docsManager.DocumentAdded+=Lambda( doc:Ted2Document )
+			
 			AddRecentFile( doc.Path )
 			
 			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()
 		End
 		
 		_docsManager.DocumentRemoved+=Lambda( doc:Ted2Document )
+			
 			If IsTmpPath( doc.Path ) DeleteFile( doc.Path )
 			SaveState()
 		End
 		
 		_recentViewedFiles=New RecentFiles( _docsManager )
 		
+		
 		'Build tab
 		
-		_buildConsole=New ConsoleExt
+		_buildConsole=Di.Resolve<BuildConsole>()
 		' jump to errors etc.
 		_buildConsole.RequestJumpToFile+=Lambda( path:String,line:Int )
 			
@@ -96,7 +104,7 @@ Class MainWindowInstance Extends Window
 		
 		'Output tab
 		
-		_outputConsole=New ConsoleExt
+		_outputConsole=Di.Resolve<OutputConsole>()
 		Local bar:=New ToolBarExt
 		bar.MaxSize=New Vec2i( 300,30 )
 		
@@ -151,7 +159,7 @@ Class MainWindowInstance Extends Window
 		
 		'Help tab
 		
-		_helpView=New HtmlViewExt
+		_helpView=Di.Resolve<HelpView>()
 		_docsConsole=New DockingView
 		bar=New ToolBarExt
 		bar.MaxSize=New Vec2i( 300,30 )
@@ -185,7 +193,7 @@ Class MainWindowInstance Extends Window
 			label.Text=url
 		End
 		
-		_helpTree=New HelpTreeView( _helpView )
+		_helpTree=Di.Resolve<HelpTreeView>()
 		_docsConsole.AddView( _helpTree,"right",200,True )
 		
 		_docsConsole.AddView( bar,"top" )
@@ -206,9 +214,9 @@ Class MainWindowInstance Extends Window
 		_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[] )
 			
 			ShowBuildConsole( True )
@@ -217,10 +225,10 @@ Class MainWindowInstance Extends Window
 		
 		' ProjectView
 		'
-		_projectView=New ProjectView( _docsManager,_buildActions )
+		_projectView=Di.Resolve<ProjectView>()
 		' project opened
-		_projectView.ProjectOpened+=Lambda( dir:String )
-			AddRecentProject( dir )
+		_projectView.ProjectOpened+=Lambda( path:String )
+			AddRecentProject( path )
 			SaveState()
 		End
 		' project closed
@@ -230,6 +238,14 @@ Class MainWindowInstance Extends Window
 			_findActions.FindInFiles( folder )
 		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 )
 		_editActions=New EditActions( _docsManager )
 		_findActions=New FindActions( _docsManager,_projectView,_findConsole )
@@ -247,7 +263,7 @@ Class MainWindowInstance Extends Window
 		_tabMenu.AddAction( _fileActions.saveAs )
 		_tabMenu.AddSeparator()
 		_tabMenu.AddAction( _buildActions.lockBuildFile )
-		
+		_tabMenu.AddAction( _projectView.setMainFile )
 		_tabMenu.AddSeparator()
 		_tabMenu.AddAction( "Open on Desktop" ).Triggered=Lambda()
 			
@@ -309,7 +325,8 @@ Class MainWindowInstance Extends Window
 		_fileMenu.AddAction( _fileActions.saveAs )
 		_fileMenu.AddAction( _fileActions.saveAll )
 		_fileMenu.AddSeparator()
-		_fileMenu.AddAction( _projectView.openProject )
+		_fileMenu.AddAction( _projectView.openProjectFolder )
+		_fileMenu.AddAction( _projectView.openProjectFile )
 		_fileMenu.AddSubMenu( _recentProjectsMenu )
 		_fileMenu.AddSubMenu( _closeProjectMenu )
 		_fileMenu.AddSeparator()
@@ -534,6 +551,18 @@ Class MainWindowInstance Extends Window
 		'SDL_RaiseWindow( SDLWindow )
 	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()
 		
 		If Not _findReplaceView.Visible Return False
@@ -635,9 +664,14 @@ Class MainWindowInstance Extends Window
 		Return _docsManager
 	End
 	
+	Property ProjView:ProjectView()
+	
+		Return _projectView
+	End
+	
 	Property LockedDocument:CodeDocument()
 	
-		Return _buildActions.LockedDocument
+		Return _docsManager.LockedDocument
 	End
 	
 	Property IsTerminating:Bool()
@@ -949,7 +983,7 @@ Class MainWindowInstance Extends Window
 		_toolBar.AddSeparator()
 		_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_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" ) )
 		_saveItem=_toolBar.AddIconicButton( icons,_fileActions.save.Triggered,saveTitle )
 		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
 		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
 		If searchMode
-			Local ident:=doc.WordAtCursor
+			Local ident:=view.WordAtCursor
 			_helpTree.QuickHelp( ident )
 			ShowDocsView( True )
 			Return
 		Endif
 		
-		Local ident:=doc.FullIdentAtCursor
+		Local ident:=view.IdentBeforeCursor()
 		
 		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
 			
@@ -1126,7 +1160,7 @@ Class MainWindowInstance Extends Window
 			
 			__prevPath=path
 			
-		ElseIf KeywordsManager.Get( doc.FileType ).Contains( ident )
+		Elseif KeywordsManager.Get( view.FileType ).Contains( ident )
 			
 			ShowStatusBarText( "(keyword) "+ident )
 		
@@ -1232,7 +1266,7 @@ Class MainWindowInstance Extends Window
 	
 	Method GotoDeclaration()
 	
-		Local doc:=Cast<CodeDocument>( _docsManager.CurrentDocument )
+		Local doc:=_docsManager.CurrentCodeDocument
 		If Not doc Return
 		
 		doc.GotoDeclaration()
@@ -1307,9 +1341,9 @@ Class MainWindowInstance Extends Window
 	End
 
 	Method OpenDocument( path:String,lockIt:Bool=False )
-	
+		
 		_docsManager.OpenDocument( path,True )
-		If lockIt Then _buildActions.LockBuildFile()
+		If lockIt Then _docsManager.LockBuildFile()
 		UpdateWindow( True )
 	End
 	
@@ -1361,18 +1395,6 @@ Class MainWindowInstance Extends Window
 		_docsTabView.EnsureVisibleCurrentTab()
 	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()
 		
 		_fileActions.quit.Trigger()
@@ -1393,7 +1415,7 @@ Class MainWindowInstance Extends Window
 	
 	Method OnProjectClosed( dir:String )
 		
-		UpdateCloseProjectMenu( dir )
+		UpdateCloseProjectMenu()
 		
 		Local list:=New Stack<Ted2Document>
 		' close all related files
@@ -1626,11 +1648,11 @@ Class MainWindowInstance Extends Window
 		
 		App.Idle+=Lambda() 'delay execution
 			
-			_docsManager.LoadState( jobj )
 			_buildActions.LoadState( jobj )
 			_projectView.LoadState( jobj )
-		 
-			If Not _projectView.OpenProjects _projectView.OpenProject( CurrentDir() )
+			_docsManager.LoadState( jobj )
+		 	
+			If Not _projectView.HasOpenedProjects Then _projectView.OpenProject( CurrentDir() )
 			
 			UpdateRecentFilesMenu()
 			UpdateRecentProjectsMenu()
@@ -1653,7 +1675,7 @@ Class MainWindowInstance Extends Window
 	Protected
 	
 	Method OnKeyEvent( event:KeyEvent ) Override
-	
+		
 		Select event.Type
 		Case EventType.KeyDown
 			
@@ -1697,7 +1719,7 @@ Class MainWindowInstance Extends Window
 	End
 	
 	Method OnWindowEvent( event:WindowEvent ) Override
-
+		
 		Select event.Type
 			
 			Case EventType.WindowClose
@@ -1793,6 +1815,7 @@ Class MainWindowInstance Extends Window
 	Field _storedSize:Recti,_storedMaximized:Bool
 	
 	Field _recentViewedFiles:RecentFiles
+	Field _codeParsing:CodeParsing
 	
 	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 ) ) )
@@ -1821,7 +1844,7 @@ Class MainWindowInstance Extends Window
 		If _recentProjects.Length>10 Then _recentProjects.Resize( 10 )
 		
 		UpdateRecentProjectsMenu()
-		UpdateCloseProjectMenu( path )
+		UpdateCloseProjectMenu()
 	End
 	
 	Method UpdateRecentFilesMenu()
@@ -1844,34 +1867,31 @@ Class MainWindowInstance Extends Window
 	End
 	
 	Method UpdateRecentProjectsMenu()
-	
+		
 		_recentProjectsMenu.Clear()
-	
+		
 		Local recents:=New StringStack
-	
+		
 		For Local path:=Eachin _recentProjects
-			If GetFileType( path )<>FileType.Directory Continue
-	
 			_recentProjectsMenu.AddAction( path ).Triggered=Lambda()
 				_projectView.OpenProject( path )
 			End
-	
 			recents.Add( path )
 		Next
-	
+		
 		_recentProjects=recents
 	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()
 			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
 
 	Property Name:String() Virtual
-	
+		
 		Return "<untitled plugin>"
 	End
 	

+ 4 - 1
Prefs.monkey2

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

+ 33 - 26
Ted2.monkey2

@@ -63,6 +63,7 @@
 #Import "parser/Parser"
 #Import "parser/Monkey2Parser"
 #Import "parser/ParserPlugin"
+#Import "parser/CodeParsing"
 
 #Import "product/BuildProduct"
 #Import "product/Mx2ccEnv"
@@ -120,7 +121,7 @@
 #Import "view/Undock"
 #Import "view/TextViewExt"
 
-
+#Import "PathsProvider"
 #Import "Tree"
 #Import "Tuple"
 #Import "Plugin"
@@ -130,6 +131,9 @@
 #Import "LiveTemplates"
 #Import "MainWindow"
 
+#Import "di/Di"
+#Import "di/DiSetup"
+
 
 Namespace ted2go
 
@@ -139,13 +143,16 @@ Using mojox..
 Using tinyxml2..
 Using sdl2..
 
+
 Const MONKEY2_DOMAIN:="http://monkeycoder.co.nz"
 
-Global AppTitle:="Ted2Go v2.10"
+Global AppTitle:="Ted2Go v2.11"
 
 
 Function Main()
-
+	
+	SetupDiContainer()
+	
 	Prefs.LoadLocalState()
 	
 	Local root:=Prefs.MonkeyRootPath
@@ -165,19 +172,23 @@ Function Main()
 	'
 	Local jobj:=JsonObject.Load( "bin/ted2.state.json" )
 	If Not jobj jobj=New JsonObject
-
+	
 	Prefs.LoadState( jobj )
 	
 	'initial theme
 	'
 	If Not jobj.Contains( "theme" ) jobj["theme"]=New JsonString( "theme-hollow" )
-
+	
 	If Not jobj.Contains( "themeScale" ) jobj["themeScale"]=New JsonNumber( 1 )
 	
 	SetConfig( "MOJO_INITIAL_THEME",jobj.GetString( "theme" ) )
 	
 	SetConfig( "MOJO_INITIAL_THEME_SCALE",jobj.GetString( "themeScale" ) )
 	
+	If Prefs.OpenGlProfile
+		SetConfig( "MOJO_OPENGL_PROFILE",Prefs.OpenGlProfile )
+	Endif
+	
 	'start the app!
 	'
 	New AppInstance
@@ -185,7 +196,7 @@ Function Main()
 	'initial window state
 	'
 	Local flags:=WindowFlags.Resizable|WindowFlags.HighDPI
-
+	
 	Local rect:Recti
 	
 	If jobj.Contains( "windowRect" ) 
@@ -196,7 +207,7 @@ Function Main()
 		rect=New Recti( 0,0,w,h )
 		flags|=WindowFlags.Center
 	Endif
-
+	
 	New MainWindowInstance( AppTitle,rect,flags,jobj )
 	
 	App.Idle+=Lambda()
@@ -206,11 +217,7 @@ Function Main()
 		For Local i:=1 Until args.Length
 			Local arg:=args[i]
 			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
 	End
 	
@@ -221,7 +228,7 @@ End
 Function SetupMonkeyRootPath:String( rootPath:String,searchMode:Bool )
 	
 #If __DESKTOP_TARGET__
-
+	
 	If searchMode
 		' search for desired folder
 		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() )
 		' search for choosen-by-requester folder
 		While Not found
-	
+			
 			Local ok:=Confirm( "Initializing","Monkey2 root directory isn't set.~nTo continue, you should specify it." )
 			If Not ok
 				Return ""
@@ -255,30 +262,30 @@ Function SetupMonkeyRootPath:String( rootPath:String,searchMode:Bool )
 End
 
 Function GetActionTextWithShortcut:String( action:Action )
-
+	
 	Return action.Text+" ("+action.HotKeyText+")"
 End
 
 Function Exec( exePath:String,args:String="" )
-
+	
 #If __HOSTOS__="windows"
-
+	
 	libc.system( exePath+" "+args )
 	
 #Else If __HOSTOS__="macos"
-
+	
 	libc.system( "open ~q"+exePath+"~q --args "+args )
-
+	
 #Else If __HOSTOS__="linux"
-
+	
 	libc.system( exePath+" "+args+" >/dev/null 2>/dev/null &" )
-
+	
 #Else If __HOSTOS__="raspbian"
-
+	
 	libc.system( exePath+" "+args+" >/dev/null 2>/dev/null &" )
-
+	
 #Endif
-
+	
 End
 
 
@@ -291,13 +298,13 @@ Function FindBinFolder:String( startingFolder:String )
 	ChangeDir( startingFolder )
 	
 	While GetFileType( "bin" )<>FileType.Directory Or GetFileType( "modules" )<>FileType.Directory
-	
+		
 		If IsRootDir( CurrentDir() )
 			
 			ok=False
 			Exit
 		Endif
-	
+		
 		ChangeDir( ExtractDir( CurrentDir() ) )
 	Wend
 	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
 		_debugView=debugView
 		
-		_docs.DocumentRemoved+=Lambda( doc:Ted2Document )
-
-			If doc=_locked _locked=Null
-		End
-		
 		buildAndRun=New Action( "Run" )
 #If __TARGET__="macos"
 		buildAndRun.HotKey=Key.R
@@ -111,7 +106,7 @@ Class BuildActions Implements IModuleBuilder
 		nextError.HotKey=Key.F4
 		
 		lockBuildFile=New Action( "Lock build file" )
-		lockBuildFile.Triggered=LockBuildFile
+		lockBuildFile.Triggered=_docs.LockBuildFile
 		lockBuildFile.HotKey=Key.L
 		lockBuildFile.HotKeyModifiers=Modifier.Menu
 		
@@ -208,41 +203,22 @@ Class BuildActions Implements IModuleBuilder
 		Endif
 	End
 	
-	Property LockedDocument:CodeDocument()
-	
-		Return _locked
-	End
-	
 	Property Verbosed:Bool()
 	
 		Return _verboseMode.Checked
 	End
 	
-	Method LockBuildFile()
-		
-		Local doc:=Cast<CodeDocument>( _docs.CurrentDocument )
-		OnLockBuildFile( doc )
-	End
-	
 	Method SaveState( jobj:JsonObject )
 		
-		If _locked jobj["lockedDocument"]=New JsonString( _locked.Path )
-		
 		jobj["buildConfig"]=New JsonString( _buildConfig )
 		
 		jobj["buildTarget"]=New JsonString( _buildTarget )
 		
 		jobj["buildVerbose"]=New JsonBool( _verboseMode.Checked )
 	End
-		
+	
 	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" )
 			_buildConfig=jobj["buildConfig"].ToString()
 			Select _buildConfig
@@ -290,7 +266,7 @@ Class BuildActions Implements IModuleBuilder
 		Wend
 	
 		Local idle:=Not _console.Running
-		Local canbuild:=idle And BuildDoc()<>Null And _buildTarget
+		Local canbuild:=idle And FilePathToBuild And _buildTarget
 		
 		build.Enabled=canbuild
 		buildAndRun.Enabled=canbuild
@@ -378,8 +354,6 @@ Class BuildActions Implements IModuleBuilder
 	Field _console:ConsoleExt
 	Field _debugView:DebugView
 	
-	Field _locked:CodeDocument
-	
 	Field _errors:=New List<BuildError>
 	
 	Field _buildConfig:String
@@ -401,11 +375,14 @@ Class BuildActions Implements IModuleBuilder
 	Field _storedTargets:String
 	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
 	
 	Method SaveAll:Bool( buildFile:String )
@@ -564,10 +541,10 @@ Class BuildActions Implements IModuleBuilder
 	
 	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
 		
 		Local opts:=product.GetMx2ccOpts()
@@ -581,12 +558,12 @@ Class BuildActions Implements IModuleBuilder
 		If Verbosed cmd+=" -verbose"
 		cmd+=" -config="+config
 		cmd+=" -target="+target
-		cmd+=" ~q"+buildDoc.Path+"~q"
+		cmd+=" ~q"+buildDocPath+"~q"
 		
 		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.")
 		
@@ -662,41 +639,16 @@ Class BuildActions Implements IModuleBuilder
 		If _errors.Empty Return
 		
 		_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
 	
 	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
 	
 	Method OnUpdateModules()

+ 1 - 1
action/FindActions.monkey2

@@ -56,7 +56,7 @@ Class FindActions
 			Local path:=docs.CurrentDocument?.Path
 			If Not path Then path=projView.SelectedItem?.Path
 			
-			Local proj:=projView.FindProjectByFile( path )
+			Local proj:=ProjectView.FindProject( path )?.Folder
 			OnFindInFiles( "",proj )
 			
 		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
 			
-			Local projs:=projView.OpenProjects
+			Local projs:=projView.OpenProjectsFolders
 			If Not projs Return
 			
 			_projList.RemoveAllItems()

+ 6 - 0
dialog/PrefsDialog.monkey2

@@ -92,6 +92,7 @@ Class PrefsDialog Extends DialogExt
 	Field _mainProjectIcons:CheckButton
 	Field _mainProjectSingleClickExpanding:CheckButton
 	Field _mainPlaceDocsAtBegin:CheckButton
+	Field _mainUseOpenGlEsProfile:CheckButton
 	
 	Field _monkeyRootPath:TextFieldExt
 	
@@ -147,6 +148,7 @@ Class PrefsDialog Extends DialogExt
 		Prefs.MainProjectIcons=_mainProjectIcons.Checked
 		Prefs.MainProjectSingleClickExpanding=_mainProjectSingleClickExpanding.Checked
 		Prefs.MainPlaceDocsAtBegin=_mainPlaceDocsAtBegin.Checked
+		Prefs.OpenGlProfile=_mainUseOpenGlEsProfile.Checked ? "es" Else ""
 		
 		App.ThemeChanged()
 		
@@ -174,6 +176,9 @@ Class PrefsDialog Extends DialogExt
 		_mainPlaceDocsAtBegin=New CheckButton( "Place opened document to the left side" )
 		_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.Enabled=False
 		Local chooseMonkeyPath:=New Action( "..." )
@@ -213,6 +218,7 @@ Class PrefsDialog Extends DialogExt
 		docker.AddView( _mainToolBarVisible,"top" )
 		docker.AddView( _mainProjectSingleClickExpanding,"top" )
 		docker.AddView( _mainPlaceDocsAtBegin,"top" )
+		docker.AddView( _mainUseOpenGlEsProfile,"top" )
 		docker.AddView( New Label( " " ),"top" )
 		
 		Return docker

+ 40 - 131
document/CodeDocument.monkey2

@@ -426,7 +426,7 @@ Class CodeDocumentView Extends Ted2CodeTextView
 							If text.ToLower().EndsWith( "abstract" )
 								' nothing
 							Else
-								Local scope:=_doc.Parser.GetScope( FilePath,LineNumAtCursor )
+								Local scope:=_doc.Parser.GetScope( FilePath,CursorPos )
 								If scope And scope.Kind=CodeItemKind.Interface_
 									' nothing
 								Else
@@ -759,10 +759,11 @@ Class CodeDocumentView Extends Ted2CodeTextView
 	End
 	
 	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 i:=str.Find( "{" )
@@ -782,7 +783,6 @@ Class CodeDocumentView Extends Ted2CodeTextView
 			tv.Text=str
 			dock.AddView( tv,"bottom",200,True )
 			
-			
 			Local dialog:=New Dialog( "ParseInfo",dock )
 			dialog.AddAction( "Close" ).Triggered=dialog.Close
 			dialog.MinSize=New Vec2i( 512,600 )
@@ -1140,7 +1140,7 @@ Class CodeDocument Extends Ted2Document
 						Local s:=textLine.Slice( 0,i1 ).Trim()
 						s=Utils.GetIndentBeforePos( s,s.Length )
 						
-						Local item:=_parser.ItemAtScope( s,Path,_codeView.LineNumAtCursor )
+						Local item:=_parser.ItemAtScope( s,Path,CursorPos )
 						If item
 							' strip ident
 							s=item.Text.Slice( item.Ident.Length )
@@ -1245,6 +1245,21 @@ Class CodeDocument Extends Ted2Document
 		Return _errMap[line]
 	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()
 		_errors.Clear()
 		_errMap.Clear()
@@ -1266,9 +1281,8 @@ Class CodeDocument Extends Ted2Document
 		
 		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
 			Local pos:=item.ScopeStartPos
@@ -1439,6 +1453,12 @@ Class CodeDocument Extends Ted2Document
 	Field _toolBar:ToolBarExt
 	Field _content:DockingView
 	
+	Method InitParser()
+		
+		_parser=ParsersManager.Get( FileExtension )
+		_parsingEnabled=Not ParsersManager.IsFake( _parser )
+	End
+	  
 	Method GetToolBar:ToolBarExt()
 		
 		If _toolBar Return _toolBar
@@ -1536,8 +1556,6 @@ Class CodeDocument Extends Ted2Document
 		
 		InitParser()
 		
-		ParsingDoc() 'start parsing right after loading, not by timer
-		
 		' grab lines after load
 		_doc.LinesModified+=Lambda( first:Int,removed:Int,inserted:Int )
 			
@@ -1585,17 +1603,24 @@ Class CodeDocument Extends Ted2Document
 		OnUpdateCurrentScope()
 	End
 	
+	Property CursorPos:Vec2i()
+		
+		Return GetCursorPos( _codeView )
+	End
+	
 	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
 			_treeView.SelectByScope( scope )
 		Endif
 	End
 	
-	Method UpdateCodeTree()
+	Method UpdateCodeTree( codeItems:Stack<CodeItem> = Null )
 		
-		_treeView.Fill( FileExtension,Path )
+		_treeView.Fill( codeItems,_parser )
 		OnUpdateCurrentScope()
 	End
 	
@@ -1639,114 +1664,8 @@ Class CodeDocument Extends Ted2Document
 		Next
 	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()
 		
-		' catch for common operations
-		
-		
-		' -----------------------------------
-		' catch for parsing
-		
-		ParsingOnTextChanged()
-		
 	End
 	
 	Method OnCursorChanged()
@@ -1884,9 +1803,8 @@ Class CodeDocument Extends Ted2Document
 			
 			opts.ident=ident
 			opts.filePath=Path
-			opts.docLineNum=_codeView.LineNumAtCursor
+			opts.cursor=CursorPos
 			opts.docLineStr=line
-			opts.docPosInLine=pos
 			opts.results=results
 			
 			results.Clear()
@@ -2375,12 +2293,3 @@ Class ParamsHintView Extends TextView
 	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 CurrentDocumentChanged:Void()
+	Field LockedDocumentChanged:Void()
 	
 	Field DocumentAdded:Void( doc:Ted2Document )
 	Field DocumentRemoved:Void( doc:Ted2Document )
@@ -34,22 +35,43 @@ Class DocumentManager
 		nextDocument.Triggered=OnNextDocument
 		nextDocument.HotKey=Key.Tab
 		nextDocument.HotKeyModifiers=Modifier.Control
-
+		
 		prevDocument=New Action( "Previous tab" )
 		prevDocument.Triggered=OnPrevDocument
 		prevDocument.HotKey=Key.Tab
 		prevDocument.HotKeyModifiers=Modifier.Control|Modifier.Shift
 		
+		DocumentRemoved+=Lambda( doc:Ted2Document )
+			
+			If doc=_locked Then OnLockedChanged( Null )
+		End
+		
 		App.Activated+=Lambda()
 			New Fiber( OnAppActivated )
 		End
 	End
 	
+	Method LockBuildFile()
+	
+		Local doc:=Cast<CodeDocument>( CurrentDocument )
+		OnLockBuildFile( doc )
+	End
+	
+	Property LockedDocument:CodeDocument()
+	
+		Return _locked
+	End
+	
 	Property TabView:TabViewExt()
-
+	
 		Return _tabView
 	End
 
+	Property CurrentCodeDocument:CodeDocument()
+		
+		Return Cast<CodeDocument>( _currentDoc )
+	End
+	
 	Property CurrentDocument:Ted2Document()
 	
 		Return _currentDoc
@@ -87,10 +109,20 @@ Class DocumentManager
 	End
 	
 	Property OpenDocuments:Ted2Document[]()
-		
+	
 		Return _openDocs.ToArray()
 	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()
 		
 		Return DocumentLabel( CurrentDocument )
@@ -144,7 +176,7 @@ Class DocumentManager
 		
 		doc=docType.CreateDocument( path )
 		If Not doc.Load() Return Null
-
+		
 		InitDoc( doc )
 		
 		Local addAtBegin:=(openByHand And Prefs.MainPlaceDocsAtBegin)
@@ -255,8 +287,10 @@ Class DocumentManager
 		jobj["openDocuments"]=docs
 		
 		If _currentDoc jobj["currentDocument"]=New JsonString( _currentDoc.Path )
-	End
 		
+		If _locked jobj["lockedDocument"]=New JsonString( _locked.Path )
+	End
+	
 	Method LoadState( jobj:JsonObject )
 		
 		If Not jobj.Contains( "openDocuments" ) Return
@@ -288,22 +322,33 @@ Class DocumentManager
 			CurrentDocument=_openDocs[0]
 		Endif
 		
+		If jobj.Contains( "lockedDocument" )
+			Local path:=jobj["lockedDocument"].ToString()
+			OnLockBuildFile( Cast<CodeDocument>( FindDocument( path ) ) )
+		Endif
+		
 	End
-
+	
 	Method Update()
 		
 		nextDocument.Enabled=_openDocs.Length>1
 		prevDocument.Enabled=_openDocs.Length>1
 	End
 	
+	Method SetAsMainFile( path:String,set:Bool )
+		
+		Local doc:=Cast<CodeDocument>( FindDocument( path ) )
+		If doc Then SetMainFileState( doc,set )
+	End
+	
+	
 	Private
 	
 	Field _tabView:TabViewExt
 	Field _browser:DockingView
-	
 	Field _currentDoc:Ted2Document
-	
 	Field _openDocs:=New Stack<Ted2Document>
+	Field _locked:CodeDocument
 	
 	Method InitDoc( doc:Ted2Document )
 	
@@ -323,7 +368,7 @@ Class DocumentManager
 		
 			Local index:=_tabView.TabIndex( doc.View )
 			If index=-1 Return	'in case doc already removed via Rename.
-
+		
 			_tabView.RemoveTab( index )
 			_openDocs.Remove( doc )
 			
@@ -438,7 +483,7 @@ Class DocumentManager
 			Case FileType.None
 			
 				doc.Dirty=True
-
+				
 				'CurrentDocument=doc
 				
 				Alert( "File '"+doc.Path+"' has been deleted!" )
@@ -449,4 +494,39 @@ Class DocumentManager
 		
 	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

+ 27 - 27
eventfilter/Monkey2KeyEventFilter.monkey2

@@ -31,33 +31,33 @@ Class Monkey2KeyEventFilter Extends TextViewKeyEventFilter
 					MainWindow.GotoDeclaration()
 					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
 			
 			

+ 1 - 0
parser/CodeItem.monkey2

@@ -491,6 +491,7 @@ Struct CodeParam
 	End
 	Field type:CodeType
 	Field params:CodeParam[] 'for func as param
+	Field srcpos:Vec2i
 	
 	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
 	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"
-		Return ident.Slice( 0,2 ).ToUpper()+ident.Slice( 2 )
+		Return Capitalize( ident,2 )
 	Case "typeinfo"
 		Return "TypeInfo"
 	End
@@ -47,7 +47,7 @@ Class Monkey2Parser Extends CodeParserPlugin
 			ParseModules()
 			
 			time=Millisecs()-time
-			'Print "parse modules: "+time+" ms"
+			Print "completed parse modules: "+(time/1000)+" sec"
 			
 			OnDoneParseModules( time )
 		End )
@@ -90,48 +90,57 @@ Class Monkey2Parser Extends CodeParserPlugin
 		Return True
 	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
-		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 )
 
-		Local json:=str.Slice( i )
-		Local jobj:=JsonObject.Parse( json )
-		
 		Local nspace:= jobj.Contains( "namespace" ) ? jobj["namespace"].ToString() Else ""
 		
 		If jobj.Contains( "members" )
@@ -163,8 +172,8 @@ Class Monkey2Parser Extends CodeParserPlugin
 					Endif
 				Endif
 				file=folder+file
-				'Print "parse import: "+file+"  mod: "+Int(isModule)
-				ParseFile( file,file,moduleName )
+				'Print "parse import: "+file
+				ParseFile( file,moduleName,False )
 			Next
 		Endif
 		
@@ -183,17 +192,17 @@ Class Monkey2Parser Extends CodeParserPlugin
 		
 		Return Null
 	End
-		
+	
 	Method ParseJsonMembers( members:Stack<JsonValue>,parent:CodeItem,filePath:String,resultContainer:Stack<CodeItem>,namespac:String )
 		
 		For Local val:=Eachin members
 		
 			Local jobj:=val.ToObject()
 			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 )
 			
@@ -211,48 +220,69 @@ Class Monkey2Parser Extends CodeParserPlugin
 			item.KindStr=kind
 			item.Access=GetAccess( flags )
 			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.IsIfaceMember=(flags & Flags.DECL_IFACEMEMBER <> 0)
 			'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
-				
-				' 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" )
 				Local sup:=jobj["superType"].ToObject()
@@ -269,7 +299,12 @@ Class Monkey2Parser Extends CodeParserPlugin
 				Next
 			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 )
 				If parent.IsExtension
 					AddExtensionItem( parent,item )
@@ -280,6 +315,15 @@ Class Monkey2Parser Extends CodeParserPlugin
 				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" )
 				Local memb:=jobj["members"].ToArray()
 				ParseJsonMembers( memb,item,filePath,resultContainer,namespac )
@@ -298,83 +342,34 @@ Class Monkey2Parser Extends CodeParserPlugin
 		Return Not IsPosInsideOfQuotes( line,posInLine )
 	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
 		
 	End
 	
-	Method GetNearestScope:CodeItem( docPath:String,docLine:Int,above:Bool )
+	Method GetNearestScope:CodeItem( docPath:String,cursor:Vec2i )
 	
 		Local items:=ItemsMap[docPath]
-	
+		
 		If Not items Return Null
-	
+		
 		' 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
-			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
 			Next
 		End
@@ -383,13 +378,13 @@ Class Monkey2Parser Extends CodeParserPlugin
 	
 	End
 	
-	Method ItemAtScope:CodeItem( ident:String,filePath:String,docLine:Int )
+	Method ItemAtScope:CodeItem( ident:String,filePath:String,cursor:Vec2i )
 		
 		Local opts:=New ParserRequestOptions
 		opts.results=New Stack<CodeItem>
 		opts.ident=ident
 		opts.filePath=filePath
-		opts.docLineNum=docLine
+		opts.cursor=cursor
 		opts.usingsFilter=_lastUsingsFilter
 		opts.intelliIdent=False
 		
@@ -444,9 +439,60 @@ Class Monkey2Parser Extends CodeParserPlugin
 		
 	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
 	
+	Const LOCAL_RULE_NONE:=0
+	Const LOCAL_RULE_SELF_SCOPE:=1
+	Const LOCAL_RULE_PARENT_SCOPE:=2
+	
 	Global _instance:=New Monkey2Parser
 	Field _filesTime:=New StringMap<Long>
 	Field _aliases:=New StringMap<CodeItem>
@@ -464,7 +510,7 @@ Class Monkey2Parser Extends CodeParserPlugin
 		
 		Local ident:=options.ident
 		Local filePath:=options.filePath
-		Local docLineNum:=options.docLineNum
+		Local cursor:=options.cursor
 		Local docLineStr:=options.docLineStr
 		Local target:=options.results
 		Local usingsFilter:=options.usingsFilter
@@ -479,10 +525,10 @@ Class Monkey2Parser Extends CodeParserPlugin
 		Local onlyOne:=(idents.Length=1)
 	
 		'check current scope
-		Local rootScope:=GetScope( filePath,docLineNum )
+		Local rootScope:=GetScope( filePath,cursor )
 		Local scope:=rootScope
 		
-		'If scope Print scope.Text
+		'Print "scope: "+scope?.Text
 		
 		'-----------------------------
 		' what the first ident is?
@@ -520,12 +566,12 @@ Class Monkey2Parser Extends CodeParserPlugin
 							'Print "cont1: "+i.Ident
 							Continue
 						Endif
-						If Not CheckAccessInScope( i,scope )
+						If Not CheckAccessInScope( scope,i )
 							'Print "cont2: "+i.Ident
 							Continue
 						Endif
 						' 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
 							Continue
 						Endif
@@ -581,26 +627,28 @@ Class Monkey2Parser Extends CodeParserPlugin
 				
 				For Local i:=Eachin Items
 					
+					'Local similar:=i.Ident.ToLower().StartsWith( ident )
+					
 					If Not CheckUsingsFilter( i.Namespac,usingsFilter )
-						'Print "skip 1 "+i.Ident
+						'If similar Print "exclude: "+i.Namespac+" "+i.Text
 						Continue
 					Endif
 					
 					'Print "global 1: "+i.Scope
 					If Not CheckIdent( i.Ident,firstIdent,onlyOne,intelliIdent )
-						'Print "skip 2 "+i.Ident
+						'If similar Print "skip 2 "+i.Ident
 						Continue
 					Endif
 					
 					If Not CheckAccessInGlobal( i,filePath )
-						'Print "skip 3 "+i.Ident
+						'If similar Print "skip 3 "+i.Ident
 						Continue
 					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"
 					If Not onlyOne
@@ -635,7 +683,7 @@ Class Monkey2Parser Extends CodeParserPlugin
 		If item = Null
 			
 			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 ns := tuple ? (tuple.Item1 ? tuple.Item1 Else tuple.Item2) Else Null
 			If ns
@@ -791,12 +839,15 @@ Class Monkey2Parser Extends CodeParserPlugin
 		
 		Local al:=_aliases[typeName]
 		Return al ? al.Type.ident Else typeName
+		
 	End
 	
 	Method AddExtensionItem( parent:CodeItem,item:CodeItem )
 		
 		Local key:=parent.Ident
+		
 		Local list:=ExtraItemsMap[key]
+		
 		If Not list
 			list=New Stack<CodeItem>
 			ExtraItemsMap[key]=list
@@ -807,6 +858,7 @@ Class Monkey2Parser Extends CodeParserPlugin
 				Exit
 			Endif
 		Next
+		
 		item.IsExtension=True
 		list.Add( item )
 	End
@@ -832,16 +884,14 @@ Class Monkey2Parser Extends CodeParserPlugin
 		ted2go.ExtractExtensionItems( _extensions,item,target )
 	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
 	
 	Method ParseModules()
@@ -852,6 +902,7 @@ Class Monkey2Parser Extends CodeParserPlugin
 		
 		' pop up some modules to parse them first
 		Local dirs:=New Stack<String>
+		
 		dirs.AddAll( dd )
 		Local mods:=New String[]( "std","mojo","monkey" )
 		For Local m:=Eachin mods
@@ -865,16 +916,79 @@ Class Monkey2Parser Extends CodeParserPlugin
 				'Print "module: "+file
 				If GetFileType( file )=FileType.File
 					OnParseModule( file )
-					ParseFile( file,file,d )
+					ParseFile( file,d )
 				Endif
 			Endif
 		Next
 		
 	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 )
 		
-		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
 		
@@ -1032,6 +1146,7 @@ Class Monkey2Parser Extends CodeParserPlugin
 			Local jparam:=param.ToObject()
 			Local p:=New CodeParam
 			p.ident=jparam["ident"].ToString()
+			p.srcpos=GetScopePosition( jparam["srcpos"].ToString() )
 			p.type=ParseType( jparam )
 			' try recursive extraction
 			p.params=ParseParams( jparam )
@@ -1080,43 +1195,25 @@ Class Monkey2Parser Extends CodeParserPlugin
 		Return type
 	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
 		
 		Local result:CodeItem=Null
-		Local storedPos:=dir>0 ? 999999999 Else -1
 		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
-				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
-				
-			End
-			
+			Endif
 		Next
+		
 		Return result
 	End
 	
@@ -1137,6 +1234,39 @@ Class Monkey2Parser Extends CodeParserPlugin
 		Return False
 	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 )
 		
 		Local checkUnique:=Not target.Empty
@@ -1171,7 +1301,7 @@ Class Monkey2Parser Extends CodeParserPlugin
 		
 	End
 	
-	Method CheckAccessInScope:Bool( item:CodeItem,parent:CodeItem )
+	Method CheckAccessInScope:Bool( parent:CodeItem,item:CodeItem )
 		
 		' always show public members
 		Local a:=item.Access
@@ -1237,6 +1367,31 @@ Class Monkey2Parser Extends CodeParserPlugin
 		
 	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 )
 	
 		If ident2="" Return True
@@ -1275,8 +1430,8 @@ Class Monkey2Parser Extends CodeParserPlugin
 	
 	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_
 	End
 	
@@ -1428,13 +1583,14 @@ Class NSpace
 		Next
 		
 		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 )
 		
 	End
@@ -1506,7 +1662,7 @@ Function GetLiteralType:String( typeIdent:String )
 		Return "Float"
 	Else
 		typeIdent=typeIdent.ToLower()
-		If typeIdent = "true" Or typeIdent = "false" Return "Bool"
+		If typeIdent="true" Or typeIdent="false" Return "Bool"
 	Endif
 	Return ""
 End

+ 18 - 10
parser/Parser.monkey2

@@ -6,13 +6,13 @@ Interface ICodeParser
 	
 	Method GetConstructors( item:CodeItem,target:Stack<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 IsPosInsideOfQuotes:Bool( text:String,pos: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 CheckStartsWith:Bool( ident1:String,ident2:String )
@@ -112,15 +112,23 @@ Class ParserRequestOptions Final
 	
 	Field ident:String
 	Field filePath:String
-	Field docLineNum:Int
+	Field cursor:Vec2i
 	Field results:Stack<CodeItem>
 	Field usingsFilter:Stack<String>
 	Field docLineStr:String
-	Field docPosInLine:Int
 	Field intelliIdent:=True
 	
 End
 
+Class ParseFileParams
+	
+	Field filePath:String
+	'Field paths:String[]
+	Field moduleName:String
+	Field geninfo:Bool=True
+	
+End
+
 
 Private
 
@@ -144,7 +152,7 @@ Class FakeParser Implements ICodeParser
 	
 	Method GetConstructors( item:CodeItem,target:Stack<CodeItem> )
 	End
-	Method ParseFile:String( filePath:String,pathOnDisk:String,moduleName:String )
+	Method ParseFile:String( params:ParseFileParams )
 		'do nothing
 		Return Null
 	End
@@ -154,13 +162,13 @@ Class FakeParser Implements ICodeParser
 	Method CanShowAutocomplete:Bool( line:String,posInLine:Int )
 		Return True
 	End
-	Method GetScope:CodeItem( docPath:String,docLine:Int )
+	Method GetScope:CodeItem( docPath:String,cursor:Vec2i )
 		Return Null
 	End
-	Method GetNearestScope:CodeItem( docPath:String,docLine:Int,above:Bool )
+	Method GetNearestScope:CodeItem( docPath:String,cursor:Vec2i )
 		Return Null
 	End
-	Method ItemAtScope:CodeItem( ident:String,filePath:String,docLine:Int )
+	Method ItemAtScope:CodeItem( ident:String,filePath:String,cursor:Vec2i )
 		Return Null
 	End
 	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
 
+Using std..
 
 Private
 
@@ -8,18 +9,64 @@ Private
 Class 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
+		
+	Setter( value:TestTheSame )
+		
+		Local a8:=8
+		
 	End
 	
 	Method Test( pType:String Ptr )
+		
+		For Local Y:=0 Until 20
+			Local dev:=True
+		Next
+		
 		pType->Capitalize()
 		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
 	
 	Field aPtr:Vec2i Ptr
+	Global ccc:List<Vec3f>
+	
+End
+
+Function LocalTest()
+	
+	Local img:=Image.Load( "" )
+	Local vvv:=GetVector()
 	
 End
 
+Function GetVector:Vec2i()
+
+	Return New Vec2i
+End
+
+
 Struct Vec2i Extension
 	
 	Const One := New Vec2i( 1,1 )

+ 11 - 0
utils/TextUtils.monkey2

@@ -1,6 +1,17 @@
 
 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
 	

+ 7 - 0
utils/Utils.monkey2

@@ -367,6 +367,13 @@ End
 #End
 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
 	
 	While n >= 0

+ 5 - 5
view/AutocompleteView.monkey2

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

+ 6 - 0
view/CodeTextView.monkey2

@@ -1407,3 +1407,9 @@ Class IndentationHelper Final
 	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 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
 		
 		RootNodeVisible=False
 		node.Expanded=True
 		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 )
 		Next
 		
@@ -50,6 +36,7 @@ Class CodeTreeView Extends TreeViewExt
 	Method SelectByScope( scope:CodeItem )
 		
 		Local node:=FindNode( RootNode,scope )
+		If Not node And scope.Parent Then node=FindNode( RootNode,scope.Parent )
 		If Not node Return
 		
 		node.Expanded=True
@@ -64,7 +51,7 @@ Class CodeTreeView Extends TreeViewExt
 	
 	Private
 	
-	Field _stack:=New Stack<CodeItem>
+	Field _codeItems:Stack<CodeItem>
 	
 	Method FindNode:TreeView.Node( treeNode:TreeView.Node,item:CodeItem )
 	
@@ -100,7 +87,7 @@ Class CodeTreeView Extends TreeViewExt
 		
 		' sorting only root class members
 		If item.IsLikeClass
-				
+			
 			SortItems( list )
 			
 			If ShowInherited
@@ -111,10 +98,6 @@ Class CodeTreeView Extends TreeViewExt
 					inherRoot.Children=lst
 					inherRoot.KindStr="inherited"
 					list.Insert( 0,inherRoot )
-					'For Local i:=Eachin lst
-					'	Local children:=i.Children
-					'	
-					'Next
 				Endif
 			Endif
 		End
@@ -123,7 +106,7 @@ Class CodeTreeView Extends TreeViewExt
 		
 		Local added:=New StringStack
 		For Local i:=Eachin list
-			If i.Kind = CodeItemKind.Param_ Continue
+			If i.KindStr="block" Continue
 			Local txt:=i.Text
 			If added.Contains( txt ) Continue
 			added.Add( txt )

+ 62 - 55
view/HelpTreeView.monkey2

@@ -57,8 +57,6 @@ Class HelpTreeView Extends TreeViewExt
 			MainWindow.ShowHelp( url )
 		End
 		
-		Init()
-		
 	End
 	
 	Property FindField:TextFieldExt()
@@ -148,11 +146,17 @@ Class HelpTreeView Extends TreeViewExt
 			
 			'new doc system
 			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
 		
 		FillTree()
@@ -165,9 +169,57 @@ Class HelpTreeView Extends TreeViewExt
 		_textField.MakeKeyView()
 	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
 	
+	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
 	
 		Method New( text:String,parent:TreeView.Node,page:String )
@@ -194,6 +246,10 @@ Class HelpTreeView Extends TreeViewExt
 		FillNode( RootNode,_tree.RootNode.Children )
 		
 		Sort()
+		
+		If RootNode.Children.Length=0
+			New Node( "No docs found; you can use 'Help -- Rebuild docs'.",RootNode,"" )
+		Endif
 	End
 	
 	Method FillNode( node:TreeView.Node,items:Stack<Tree.Node> )
@@ -313,53 +369,4 @@ Class HelpTreeView Extends TreeViewExt
 		Selected=_matches[_matchid]
 	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

+ 4 - 2
view/HtmlViewExt.monkey2

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

+ 4 - 3
view/Monkey2TreeView.monkey2

@@ -44,9 +44,10 @@ Class Monkey2TreeView Extends JsonTreeView
 	End
 	
 	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 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 )
 	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 s:=StripDir( dir )+" ("+dir+")"
+		Local s:=PrepareProjectName( project )
 		node.Text=s
-		node._path=dir
+		node._path=project.Folder
+		node._project=project
 		UpdateProjIcon( node )
 		_expander.Restore( node )
-		
+	
 		UpdateNode( node )
 		ApplyFilter( node )
+		
+		Local mainFile:=project.MainFilePath
+		If mainFile Then SetMainFile( mainFile,True )
+		
 	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
 		For Local n:=Eachin RootNode.Children
-			If n.Text=s
+			Local node:=Cast<Node>( n )
+			If node._project=project
 				toRemove=n
 				Exit
 			Endif
@@ -293,6 +344,10 @@ Class ProjectBrowserView Extends TreeViewExt Implements IDraggableHolder
 			Return _path
 		End
 		
+		Property Project:Monkey2Project()
+			Return _project
+		End
+		
 		Property Detachable:Bool()
 			Return GetNodeDeepLevel( Self )>1
 		End
@@ -316,12 +371,30 @@ Class ProjectBrowserView Extends TreeViewExt Implements IDraggableHolder
 			_view=view
 		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
-	
+		
 		Field _path:String
 		Field _holders:ProjectBrowserView[]
 		Field _curHolder:ProjectBrowserView
 		Field _view:View
+		Field _project:Monkey2Project
 		
 	End
 	
@@ -408,6 +481,12 @@ Class ProjectBrowserView Extends TreeViewExt Implements IDraggableHolder
 		Return True
 	End
 	
+	Method PrepareProjectName:String( project:Monkey2Project )
+		
+		Local dir:=project.Folder
+		Return StripDir( dir )+" ("+dir+")"
+	End
+	
 	Method OnDraggedInto( node:Node,name:String )
 		
 		Local path:=GetNodePath( node )+"\"+name
@@ -494,12 +573,12 @@ Class ProjectBrowserView Extends TreeViewExt Implements IDraggableHolder
 	
 	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 t:=GetFileTime( path )
+		Local t:=proj.Modified
 		If t=_filtersFileTimes[projName] Return
 		
 		_filtersFileTimes[projName]=t
@@ -507,13 +586,10 @@ Class ProjectBrowserView Extends TreeViewExt Implements IDraggableHolder
 		Local list:=GetOrCreate( _filters,projName )
 		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
 	
 	Method UpdateProjIcon( node:TreeView.Node )

+ 369 - 48
view/ProjectView.monkey2

@@ -4,13 +4,17 @@ Namespace ted2go
 
 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 MainFileChanged:Void( path:String,prevPath:String )
+	
 	Method New( docs:DocumentManager,builder:IModuleBuilder )
 	
 		_docs=docs
@@ -20,12 +24,50 @@ Class ProjectView Extends DockingView
 		
 		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()
+		
+'		_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
 	
 	Property SelectedItem:ProjectBrowserView.Node()
@@ -33,9 +75,24 @@ Class ProjectView Extends DockingView
 		Return Cast<ProjectBrowserView.Node>( _projBrowser.Selected )
 	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
 	
 	Property SingleClickExpanding:Bool()
@@ -47,24 +104,47 @@ Class ProjectView Extends DockingView
 		_projBrowser.SingleClickExpanding=value
 	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
-		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
 	
 	Method OnFileDropped:Bool( path:String )
 		
 		Local ok:=_projBrowser.OnFileDropped( path )
 		If Not ok
-			Local isFolder:=GetFileType( path )=FileType.Directory
-			If isFolder
+			If IsValidProject( path )
 				ok=True
 				OpenProject( path )
 			Endif
@@ -72,30 +152,68 @@ Class ProjectView Extends DockingView
 		Return ok
 	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
 	End
 	
 	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 )
 	End
@@ -107,7 +225,7 @@ Class ProjectView Extends DockingView
 		
 		Local jarr:=New JsonArray
 		For Local p:=Eachin _projects
-			jarr.Add( New JsonString( p ) )
+			jarr.Add( New JsonString( p.Path ) )
 		Next
 		j["openProjects"]=jarr
 		
@@ -115,6 +233,13 @@ Class ProjectView Extends DockingView
 		
 		Local selPath:=GetNodePath( _projBrowser.Selected )
 		j["selected"]=New JsonString( selPath )
+		
+		If _activeProject Then j["active"]=New JsonString( _activeProject.Path )
+	End
+	
+	Property HasOpenedProjects:Bool()
+		
+		Return _projects.Length>0
 	End
 	
 	Method LoadState( jobj:JsonObject )
@@ -126,14 +251,20 @@ Class ProjectView Extends DockingView
 		_projBrowser.LoadState( jobj,"expanded" )
 		
 		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
+			If arr.Length=1
+				jobj["active"]=New JsonString( _projects[0].Path )
+			Endif
 		Endif
 		
 		Local selPath:=Json_GetString( jobj.Data,"selected","" )
 		If selPath Then _projBrowser.SelectByPath( selPath )
+		
+		Local activePath:=Json_GetString( jobj.Data,"active","" )
+		If activePath Then SetActiveProject( activePath,False )
 	End
 	
 	
@@ -144,12 +275,47 @@ Class ProjectView Extends DockingView
 	
 	Field _docs:DocumentManager
 	Field _docker:=New DockingView
-	Global _projects:=New StringStack
+	'Global _projectFolders:=New StringStack
+	Global _projects:=New Stack<Monkey2Project>
 	Field _builder:IModuleBuilder
 	Field _projBrowser:ProjectBrowserView
-	
+	Global _activeProject:Monkey2Project
 	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 )
 		
 		_copyPath=""
@@ -225,7 +391,7 @@ Class ProjectView Extends DockingView
 				
 			Else
 				
-				If Not RequestOkay( "Really delete file '"+path+"'?" ) Print "1111" ; Return
+				If Not RequestOkay( "Really delete file '"+path+"'?" ) Return
 				
 				If DeleteFile( path )
 				
@@ -246,18 +412,40 @@ Class ProjectView Extends DockingView
 		New Fiber( work )
 	End
 	
-	Method OnOpenProject()
+	Method OnOpenProjectFolder()
 	
-		Local dir:=MainWindow.RequestDir( "Select Project Directory...","" )
+		Local dir:=MainWindow.RequestDir( "Select project folder...","" )
 		If Not dir Return
-		
+	
 		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
 	
 	Method OnOpenDocument( path:String,makeFocused:Bool,runExec:Bool=True )
 		
 		If GetFileType( path )<>FileType.File Return
-			
+		
 		New Fiber( Lambda()
 			
 			Local ext:=ExtractExt( path )
@@ -330,7 +518,7 @@ Class ProjectView Extends DockingView
 		_docker.ContentView=browser
 		
 		browser.RequestedDelete+=Lambda( node:ProjectBrowserView.Node )
-		
+			
 			DeleteItem( browser,node.Path,node )
 		End
 		
@@ -345,7 +533,7 @@ Class ProjectView Extends DockingView
 		End
 		
 		browser.FileRightClicked+=Lambda( node:ProjectBrowserView.Node )
-		
+			
 			Local menu:=New MenuExt
 			Local path:=node.Path
 			Local pasteAction:Action
@@ -353,12 +541,12 @@ Class ProjectView Extends DockingView
 			Local fileType:=GetFileType( path )
 			
 			menu.AddAction( "Open on Desktop" ).Triggered=Lambda()
-			
+				
 				Local p:=(fileType=FileType.File) ? ExtractDir( path ) Else path
 				requesters.OpenUrl( p )
 			End
 			menu.AddAction( "Copy path" ).Triggered=Lambda()
-			
+				
 				App.ClipboardText=path
 			End
 			
@@ -460,8 +648,22 @@ Class ProjectView Extends DockingView
 				
 				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()
-						
+					
+						If Not RequestOkay( "Really close project?" ) Return
+					
 						CloseProject( path )
 					End
 					
@@ -529,6 +731,12 @@ Class ProjectView Extends DockingView
 			
 			Case FileType.File
 				
+				menu.AddAction( "Set as main file" ).Triggered=Lambda()
+				
+					SetMainFile( path )
+				End
+				menu.AddSeparator()
+				
 				menu.AddAction( "Rename file" ).Triggered=Lambda()
 					
 					Local oldName:=StripDir( path )
@@ -609,3 +817,116 @@ Class ProjectView Extends DockingView
 	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