浏览代码

Merge commit '9f62f60793aa6da54056d380e13f1e0248a60a51' into develop

Mark Sibly 7 年之前
父节点
当前提交
5b8199bde7
共有 33 个文件被更改,包括 1844 次插入710 次删除
  1. 2 1
      src/ted2go/.gitignore
  2. 82 62
      src/ted2go/MainWindow.monkey2
  3. 46 0
      src/ted2go/PathsProvider.monkey2
  4. 1 1
      src/ted2go/Plugin.monkey2
  5. 4 1
      src/ted2go/Prefs.monkey2
  6. 33 26
      src/ted2go/Ted2.monkey2
  7. 4 0
      src/ted2go/Ted2Go.mx2proj
  8. 21 69
      src/ted2go/action/BuildActions.monkey2
  9. 1 1
      src/ted2go/action/FindActions.monkey2
  10. 二进制
      src/ted2go/assets/themes/project/monkey2main.png
  11. 78 0
      src/ted2go/di/Di.monkey2
  12. 74 0
      src/ted2go/di/DiSetup.monkey2
  13. 1 1
      src/ted2go/dialog/FindInFilesDialog.monkey2
  14. 6 0
      src/ted2go/dialog/PrefsDialog.monkey2
  15. 40 131
      src/ted2go/document/CodeDocument.monkey2
  16. 90 10
      src/ted2go/document/DocumentManager.monkey2
  17. 27 27
      src/ted2go/eventfilter/Monkey2KeyEventFilter.monkey2
  18. 1 0
      src/ted2go/parser/CodeItem.monkey2
  19. 331 0
      src/ted2go/parser/CodeParsing.monkey2
  20. 363 207
      src/ted2go/parser/Monkey2Parser.monkey2
  21. 18 10
      src/ted2go/parser/Parser.monkey2
  22. 0 3
      src/ted2go/project.json
  23. 47 0
      src/ted2go/testing/ParserTests.monkey2
  24. 11 0
      src/ted2go/utils/TextUtils.monkey2
  25. 7 0
      src/ted2go/utils/Utils.monkey2
  26. 5 5
      src/ted2go/view/AutocompleteView.monkey2
  27. 6 0
      src/ted2go/view/CodeTextView.monkey2
  28. 12 29
      src/ted2go/view/CodeTreeView.monkey2
  29. 62 55
      src/ted2go/view/HelpTreeView.monkey2
  30. 4 2
      src/ted2go/view/HtmlViewExt.monkey2
  31. 4 3
      src/ted2go/view/Monkey2TreeView.monkey2
  32. 94 18
      src/ted2go/view/ProjectBrowserView.monkey2
  33. 369 48
      src/ted2go/view/ProjectView.monkey2

+ 2 - 1
src/ted2go/.gitignore

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

+ 82 - 62
src/ted2go/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
src/ted2go/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
src/ted2go/Plugin.monkey2

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

+ 4 - 1
src/ted2go/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
src/ted2go/Ted2.monkey2

@@ -62,6 +62,7 @@
 #Import "parser/Parser"
 #Import "parser/Monkey2Parser"
 #Import "parser/ParserPlugin"
+#Import "parser/CodeParsing"
 
 #Import "product/BuildProduct"
 #Import "product/Mx2ccEnv"
@@ -119,7 +120,7 @@
 #Import "view/Undock"
 #Import "view/TextViewExt"
 
-
+#Import "PathsProvider"
 #Import "Tree"
 #Import "Tuple"
 #Import "Plugin"
@@ -129,6 +130,9 @@
 #Import "LiveTemplates"
 #Import "MainWindow"
 
+#Import "di/Di"
+#Import "di/DiSetup"
+
 
 Namespace ted2go
 
@@ -138,13 +142,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
@@ -164,19 +171,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
@@ -184,7 +195,7 @@ Function Main()
 	'initial window state
 	'
 	Local flags:=WindowFlags.Resizable|WindowFlags.HighDPI
-
+	
 	Local rect:Recti
 	
 	If jobj.Contains( "windowRect" )
@@ -195,7 +206,7 @@ Function Main()
 		rect=New Recti( 0,0,w,h )
 		flags|=WindowFlags.Center
 	Endif
-
+	
 	New MainWindowInstance( AppTitle,rect,flags,jobj )
 	
 	App.Idle+=Lambda()
@@ -205,11 +216,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
 	
@@ -220,7 +227,7 @@ End
 Function SetupMonkeyRootPath:String( rootPath:String,searchMode:Bool )
 	
 #If __DESKTOP_TARGET__
-
+	
 	If searchMode
 		' search for desired folder
 		Local found:=FindBinFolder( rootPath )
@@ -228,7 +235,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 ""
@@ -254,30 +261,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
 
 
@@ -290,13 +297,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
src/ted2go/Ted2Go.mx2proj

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

+ 21 - 69
src/ted2go/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
src/ted2go/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

二进制
src/ted2go/assets/themes/project/monkey2main.png


+ 78 - 0
src/ted2go/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
src/ted2go/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
src/ted2go/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
src/ted2go/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
src/ted2go/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
src/ted2go/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
src/ted2go/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
src/ted2go/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
src/ted2go/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
src/ted2go/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
src/ted2go/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
src/ted2go/project.json

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

+ 47 - 0
src/ted2go/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
src/ted2go/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
src/ted2go/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
src/ted2go/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
src/ted2go/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
src/ted2go/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
src/ted2go/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
src/ted2go/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
src/ted2go/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
src/ted2go/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
src/ted2go/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