+ if candidate == null then return null
+ return identify_file(candidate)
+ end
+
+ # cache for `identify_file` by realpath
+ private var identified_files = new HashMap[String, nullable ModulePath]
+
+ # Identify a source file
+ # Load the associated project and groups if required
+ private fun identify_file(path: String): nullable ModulePath
+ do
+ # special case for not a nit file
+ if path.file_extension != "nit" then
+ # search in known -I paths
+ var res = search_module_in_paths(null, path, self.paths)
+ if res != null then return res
+
+ # Found nothins? maybe it is a group...
+ var candidate = null
+ if path.file_exists then
+ var mgroup = get_mgroup(path)
+ if mgroup != null then
+ var owner_path = mgroup.filepath.join_path(mgroup.name + ".nit")
+ if owner_path.file_exists then candidate = owner_path
+ end
+ end
+
+ if candidate == null then
+ toolcontext.error(null, "Error: cannot find module `{path}`.")
+ return null
+ end
+ path = candidate
+ end
+
+ # Fast track, the path is already known
+ var pn = path.basename(".nit")
+ var rp = module_absolute_path(path)
+ if identified_files.has_key(rp) then return identified_files[rp]
+
+ # Search for a group
+ var mgrouppath = path.join_path("..").simplify_path
+ var mgroup = get_mgroup(mgrouppath)
+
+ if mgroup == null then
+ # singleton project
+ var mproject = new MProject(pn, model)
+ mgroup = new MGroup(pn, mproject, null) # same name for the root group
+ mgroup.filepath = path
+ mproject.root = mgroup
+ toolcontext.info("found project `{pn}` at {path}", 2)
+ end
+
+ var res = new ModulePath(pn, path, mgroup)
+ mgroup.module_paths.add(res)
+
+ identified_files[rp] = res
+ return res
+ end
+
+ # groups by path
+ private var mgroups = new HashMap[String, nullable MGroup]
+
+ # return the mgroup associated to a directory path
+ # if the directory is not a group null is returned
+ private fun get_mgroup(dirpath: String): nullable MGroup
+ do
+ var rdp = module_absolute_path(dirpath)
+ if mgroups.has_key(rdp) then
+ return mgroups[rdp]
+ end
+
+ # Hack, a group is determined by:
+ # * the presence of a honomymous nit file
+ # * the fact that the directory is named `src`
+ var pn = rdp.basename(".nit")
+ var mp = dirpath.join_path(pn + ".nit").simplify_path
+
+ var dirpath2 = dirpath
+ if not mp.file_exists then
+ if pn == "src" then
+ # With a src directory, the group name is the name of the parent directory
+ dirpath2 = rdp.dirname
+ pn = dirpath2.basename("")