From 02fa15644899c2a3feae1f161730c19666e5846e Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Thu, 23 Apr 2026 19:30:29 +0300 Subject: [PATCH] tools: fix v doc: cannot generate an HTML documentation of my own project (fixes #9170) --- .../get_parent_mod_issue_14224_test.v | 30 +++++++ cmd/tools/vdoc/document/module.v | 84 ++++++++++++++++--- 2 files changed, 101 insertions(+), 13 deletions(-) diff --git a/cmd/tools/vdoc/document/get_parent_mod_issue_14224_test.v b/cmd/tools/vdoc/document/get_parent_mod_issue_14224_test.v index a73736247..f20aa8fe3 100644 --- a/cmd/tools/vdoc/document/get_parent_mod_issue_14224_test.v +++ b/cmd/tools/vdoc/document/get_parent_mod_issue_14224_test.v @@ -15,3 +15,33 @@ fn test_get_parent_mod_stops_at_current_vmod_issue_14224() { os.write_file(os.join_path(project_dir, 'project.v'), 'module project')! assert get_parent_mod(project_dir)! == '' } + +fn test_lookup_module_with_path_finds_current_project_root_issue_9170() { + tmp_dir := os.join_path(os.vtmp_dir(), 'vdoc_lookup_module_issue_9170_${os.getpid()}') + os.rmdir_all(tmp_dir) or {} + defer { + os.rmdir_all(tmp_dir) or {} + } + project_dir := os.join_path(tmp_dir, 'issue9170') + os.mkdir_all(project_dir)! + os.write_file(os.join_path(project_dir, 'v.mod'), "Module {\n\tname: 'issue9170'\n}\n")! + os.write_file(os.join_path(project_dir, 'main.v'), 'module main\n')! + assert lookup_module_with_path('issue9170', project_dir)! == os.real_path(project_dir) +} + +fn test_lookup_module_with_path_uses_vmod_source_root_issue_9170() { + tmp_dir := os.join_path(os.vtmp_dir(), + 'vdoc_lookup_module_source_root_issue_9170_${os.getpid()}') + os.rmdir_all(tmp_dir) or {} + defer { + os.rmdir_all(tmp_dir) or {} + } + project_dir := os.join_path(tmp_dir, 'project_dir') + source_root := os.join_path(project_dir, 'src') + os.mkdir_all(source_root)! + os.write_file(os.join_path(project_dir, 'v.mod'), + "Module {\n\tname: 'issue9170'\n\tbase_url: 'src'\n}\n")! + os.write_file(os.join_path(source_root, 'main.v'), 'module main\n')! + assert lookup_module_with_path('issue9170', project_dir)! == os.real_path(source_root) + assert lookup_module_with_path('issue9170', source_root)! == os.real_path(source_root) +} diff --git a/cmd/tools/vdoc/document/module.v b/cmd/tools/vdoc/document/module.v index 8deef44da..b1122cc67 100644 --- a/cmd/tools/vdoc/document/module.v +++ b/cmd/tools/vdoc/document/module.v @@ -4,6 +4,50 @@ import os import v.ast import v.parser import v.pref +import v.vmod + +fn module_path_from_vmod_root(vmod_root string, mod string) !string { + vmod_path := os.join_path(vmod_root, 'v.mod') + if !os.is_file(vmod_path) { + return error('module not found') + } + manifest := vmod.from_file(vmod_path)! + tail_path := mod_tail_after_vmod_name(mod, manifest.name)! + mut try_path := manifest.source_root(vmod_root) + if tail_path.len > 0 { + try_path = os.join_path(try_path, tail_path) + } + if os.is_dir(try_path) && !os.is_dir_empty(try_path) { + return try_path + } + return error('module not found') +} + +fn module_path_from_search_root(search_path string, mod string) !string { + mod_path := mod.replace('.', os.path_separator) + try_path := os.join_path_single(search_path, mod_path) + if os.is_dir(try_path) && !os.is_dir_empty(try_path) { + return try_path + } + return module_path_from_vmod_root(search_path, mod) +} + +fn mod_tail_after_vmod_name(mod string, vmod_name string) !string { + if vmod_name == '' { + return error('module not found') + } + mod_parts := mod.split('.') + vmod_parts := vmod_name.split('.') + for i := 0; i + vmod_parts.len <= mod_parts.len; i++ { + if i > 1 { + break + } + if mod_parts[i..i + vmod_parts.len].join('.') == vmod_name { + return mod_parts[i + vmod_parts.len..].join(os.path_separator) + } + } + return error('module not found') +} // get_parent_mod returns the parent mod name, in dot format. // It works by climbing up the folder hierarchy, until a folder, @@ -59,21 +103,35 @@ fn get_parent_mod(input_dir string) !string { pub fn lookup_module_with_path(mod string, base_path string) !string { vexe := pref.vexe_path() vroot := os.dir(vexe) - mod_path := mod.replace('.', os.path_separator) - compile_dir := os.real_path(base_path) - modules_dir := os.join_path(compile_dir, 'modules', mod_path) - vlib_path := os.join_path(vroot, 'vlib', mod_path) - mut paths := [modules_dir, vlib_path] - vmodules_paths := os.vmodules_paths() - for vmpath in vmodules_paths { - paths << os.join_path(vmpath, mod_path) - } - for path in paths { - if !os.exists(path) || os.is_dir_empty(path) { - continue - } + mut compile_dir := os.real_path(base_path) + if !os.is_dir(compile_dir) { + compile_dir = os.dir(compile_dir) + } + if path := module_path_from_search_root(compile_dir, mod) { + return path + } + modules_dir := os.join_path(compile_dir, 'modules') + if path := module_path_from_search_root(modules_dir, mod) { return path } + mut current_dir := compile_dir + for { + parent_dir := os.dir(current_dir) + if parent_dir == current_dir { + break + } + current_dir = parent_dir + if path := module_path_from_search_root(current_dir, mod) { + return path + } + } + mut search_roots := [os.join_path(vroot, 'vlib')] + search_roots << os.vmodules_paths() + for search_root in search_roots { + if path := module_path_from_search_root(search_root, mod) { + return path + } + } return error('module "${mod}" not found.') } -- 2.39.5