Bläddra i källkod

Datafiles: manage partial reload when adding new files to data files

lviguier 1 år sedan
förälder
incheckning
045772c85c
2 ändrade filer med 236 tillägg och 8 borttagningar
  1. 233 6
      hide/comp/cdb/DataFiles.hx
  2. 3 2
      hide/comp/cdb/Editor.hx

+ 233 - 6
hide/comp/cdb/DataFiles.hx

@@ -35,8 +35,210 @@ class DataFiles {
 				loadSheet(sheet);
 	}
 
+	public static function loadFile( file : String, sheet : cdb.Sheet) @:privateAccess {
+		var sheetName = getTypeName(sheet);
+		var levelID = file.split("/").pop().split(".").shift();
+		levelID = levelID.charAt(0).toUpperCase()+levelID.substr(1);
+
+		function loadRec( p : hrt.prefab.Prefab, parent : hrt.prefab.Prefab, toRemove : Array<DataProps> ) {
+			// Initiliaze to remove list with the full list of lines data.
+			if (parent == null) {
+				toRemove = new Array<DataProps>();
+				for (ld in sheet.sheet.linesData)
+					if (ld.file == file)
+						toRemove.push(ld);
+			}
+
+			if( p.getCdbType() == sheetName ) {
+				var dprops : DataProps = {
+					file : file,
+					path : p.getAbsPath(),
+					index : 0,
+					origin : haxe.Json.stringify(p.props)
+				};
+
+				if( parent != null ) {
+					for( c in parent.children ) {
+						if( c == p ) break;
+						if( c.name == p.name ) dprops.index++;
+					}
+				}
+
+				if( sheet.idCol != null && Reflect.field(p.props,sheet.idCol.name) == "" )
+					Reflect.setField(p.props,sheet.idCol.name,levelID+"_"+p.name+(dprops.index == 0 ? "" : ""+dprops.index));
+
+				var changed = false;
+				for (idx => ld in sheet.sheet.linesData) {
+					if (ld.file == file && ld.path == p.getAbsPath()) {
+						if (ld.index == dprops.index) {
+							toRemove.remove(sheet.sheet.linesData[idx]);
+							sheet.sheet.linesData[idx] = dprops;
+							sheet.sheet.lines[idx] = p.props;
+							changed = true;
+						}
+					}
+				}
+
+				// Meaning that this is new data to add so insert it at the right index
+				if (!changed) {
+					var sepIdx = getSeparatorForPath(file, sheet);
+					var idxInsert = sheet.sheet.separators[sepIdx].index;
+
+					// Insert new line
+					sheet.sheet.linesData.insert(idxInsert, dprops);
+					sheet.sheet.lines.insert(idxInsert, p.props);
+
+					// Shift separators
+					for (idx in (sepIdx + 1)...sheet.sheet.separators.length)
+						sheet.sheet.separators[idx].index++;
+				}
+			}
+
+			for( c in p )loadRec(c,p,toRemove);
+
+			if (parent == null) {
+				for (rem in toRemove) {
+					var idxRemove = sheet.sheet.linesData.indexOf(rem);
+					sheet.sheet.linesData.remove(rem);
+					sheet.sheet.lines.remove(sheet.sheet.lines[idxRemove]);
+
+					var sepIdx = 0;
+					for (idx => s in sheet.sheet.separators) {
+						if (s.index >= idxRemove) {
+							sepIdx = idx;
+							break;
+						}
+					}
+
+					// Shift sperators
+					for (idx in (sepIdx + 1)...sheet.sheet.separators.length)
+						sheet.sheet.separators[idx].index--;
+				}
+			}
+		}
+
+		var p = loadPrefab(file);
+		loadRec(p,null,[]);
+	}
+
+	/*
+		Return the index of the corresponding separator in the sheet's separators array for a path.
+		If there's not already a separator for the path, create it.
+	*/
+	static function getSeparatorForPath(path: String, sheet: cdb.Sheet) : Int {
+		var separators = @:privateAccess sheet.sheet.separators;
+
+		function comparePath(p1 : String, p2 : String) : Int {
+			var p1Parts = p1.split("/");
+			var p2Parts = p2.split("/");
+
+			var idx = 0;
+			while (true) {
+				if (p1Parts.length <= idx || p2Parts.length <= idx || p1Parts[idx] != p2Parts[idx])
+					return idx - 1;
+
+				idx++;
+			}
+		}
+
+		function addChildSeparator(sep: cdb.Data.Separator, parentSepIdx: Int) : Int {
+			// We might want to add it following the alphabetic order and not at the last position
+			for (idx in (parentSepIdx + 1)...separators.length) {
+				var s = separators[idx];
+
+				if (s.level < sep.level) {
+					separators.insert(idx, sep);
+					return idx;
+				}
+			}
+
+			separators.push(sep);
+			return separators.length -1;
+		}
+
+		// Try to find the most matching separator for this path
+		var matchingSepData = { sepIdx: -1, level: -1 };
+		for (sIdx => s in separators) {
+			var level = comparePath(s.path, path);
+			if (level > matchingSepData.level) {
+				matchingSepData.sepIdx = sIdx;
+				matchingSepData.level = level;
+			}
+		}
+
+		// Meaning that there's already a separator for this path
+		if (matchingSepData.level == path.split("/").length - 1)
+			return matchingSepData.sepIdx;
+
+		// Meaning that there is one partial matching separator for this path
+		if (matchingSepData.level != -1) {
+			var pathSplit = path.split("/");
+			var matchingSep = separators[matchingSepData.sepIdx];
+			var matchingSepPathSplit = matchingSep.path.split("/");
+
+			// Check if the matching sep is fully matching
+			if (matchingSepPathSplit.length == matchingSepData.level + 1) {
+				var sep : cdb.Data.Separator = {};
+				sep.level = matchingSep.level + 1;
+
+				var newPath = [ for(idx in (matchingSepData.level + 1)...pathSplit.length) pathSplit[idx]].join("/");
+				sep.path = matchingSep.path + "/" + newPath;
+				sep.title = StringTools.replace(newPath, "/", " > ");
+
+				var idx = addChildSeparator(sep, matchingSepData.sepIdx);
+				sep.index = idx == separators.length - 1 ? @:privateAccess sheet.sheet.lines.length : separators[idx + 1].index - 1;
+				return idx;
+			}
+			else {
+				// Otherwise split the matching part of the separator
+
+				// Modify the most matching separator in a fully matching separator (remove the diff part of it)
+				var newPath = [ for(idx in 0...matchingSepData.level + 1) matchingSepPathSplit[idx]].join("/");
+				matchingSep.path = newPath;
+				matchingSep.title = StringTools.replace(newPath, "/", " > ");
+
+				// Create a new separator for the existings lines that were under the one we splitted
+				newPath = [ for(idx in (matchingSepData.level + 1)...matchingSepPathSplit.length) matchingSepPathSplit[idx] ].join("/");
+				var sep : cdb.Data.Separator = {
+					title : StringTools.replace(newPath, "/", " > "),
+					index : matchingSep.index,
+					level : matchingSep.level + 1,
+					path : matchingSep.path + "/" + newPath
+				};
+
+				separators.insert(matchingSepData.sepIdx + 1, sep);
+
+				// Shift levels of separators that were children of the one we splitted
+				for (idx in (matchingSepData.sepIdx + 2)...separators.length) {
+					var s = separators[idx];
+
+					if (s.level <= matchingSep.level)
+						break;
+
+					s.level++;
+				}
+
+				// Then create a new separator for the new path
+				newPath = [ for(idx in (matchingSepData.level + 1)...pathSplit.length) pathSplit[idx]].join("/");
+				var newSep : cdb.Data.Separator = {
+					title : StringTools.replace(newPath, "/", " > "),
+					level : matchingSep.level + 1,
+					path : matchingSep.path + "/" + newPath
+				};
+
+				var newSepIdx = addChildSeparator(newSep, matchingSepData.sepIdx);
+				newSep.index = newSepIdx == separators.length - 1 ? @:privateAccess sheet.sheet.lines.length : separators[newSepIdx + 1].index - 1;
+				return newSepIdx;
+			}
+		}
+
+		// Meaning that there is no matching separator and we need to create one
+		throw("Not implemented");
+		return 0;
+	}
+
 	#if (editor || cdb_datafiles)
-	static function onFileChanged() {
+	static function onFileChanged(path: String) {
 		if( skip > 0 ) {
 			skip--;
 			return;
@@ -45,8 +247,33 @@ class DataFiles {
 		haxe.Timer.delay(function() {
 			if( !changed ) return;
 			changed = false;
-			reload();
-			Editor.refreshAll(true);
+
+			// Only reload data files that are concerned by this file modification
+			function reloadFile(path: String) {
+				var fullPath = Ide.inst.getPath(path);
+				if (sys.FileSystem.isDirectory(fullPath)) {
+					var files = sys.FileSystem.readDirectory(fullPath);
+					for (f in files)
+						reloadFile(path + "/" + f);
+
+					return;
+				}
+
+				for( sheet in base.sheets ) {
+					if( sheet.props.dataFiles != null ) {
+						var dataFiles = sheet.props.dataFiles.split(";");
+						for (dataFile in dataFiles) {
+							var s = sheet.separators;
+							var reg = new EReg("^"+dataFile.split(".").join("\\.").split("*").join(".*")+"$","");
+							if (reg.match(path))
+								DataFiles.loadFile(path, sheet);
+						}
+					}
+				}
+			}
+
+			reloadFile(path);
+			Editor.refreshAll(true, false);
 		},0);
 	}
 
@@ -54,7 +281,7 @@ class DataFiles {
 		var p = Ide.inst.loadPrefab(file);
 		if( !watching.exists(file) ) {
 			watching.set(file, true);
-			Ide.inst.fileWatcher.register(file, onFileChanged);
+			Ide.inst.fileWatcher.register(file, () -> onFileChanged(file));
 		}
 		return p;
 	}
@@ -156,7 +383,7 @@ class DataFiles {
 				#if (editor || cdb_datafiles)
 				if( !watching.exists(path) ) {
 					watching.set(path, true);
-					Ide.inst.fileWatcher.register(path, onFileChanged, true);
+					Ide.inst.fileWatcher.register(path, () -> onFileChanged(path), true);
 				}
 				#end
 				var reg = new EReg("^"+part.split(".").join("\\.").split("*").join(".*")+"$","");
@@ -209,7 +436,7 @@ class DataFiles {
 		for( r in root.subs )
 			browseRec(r, 0);
 	}
-	
+
 	public static function getPrefabsByPath(prefab: hrt.prefab.Prefab, path : String ) : Array<hrt.prefab.Prefab> {
 		function rec(prefab: hrt.prefab.Prefab, parts : Array<String>, index : Int, out : Array<hrt.prefab.Prefab> ) {
 			var name = parts[index++];

+ 3 - 2
hide/comp/cdb/Editor.hx

@@ -1056,9 +1056,10 @@ class Editor extends Component {
 	}
 
 	public static var inRefreshAll(default,null) : Bool;
-	public static function refreshAll( eraseUndo = false ) {
+	public static function refreshAll( eraseUndo = false, loadDataFiles = true) {
 		var editors : Array<Editor> = [for( e in new Element(".is-cdb-editor").elements() ) e.data("cdb")];
-		DataFiles.load();
+		if (loadDataFiles)
+			DataFiles.load();
 		inRefreshAll = true;
 		for( e in editors ) {
 			e.syncSheet(Ide.inst.database);