1 module served.commands.file_search; 2 3 import served.extension; 4 import served.lsp.filereader; 5 import served.types; 6 7 import workspaced.api; 8 import workspaced.coms; 9 10 import std.algorithm : endsWith, sort, startsWith, uniq; 11 import std.path : baseName, buildNormalizedPath, buildPath, isAbsolute, stripExtension; 12 import std.string : makeTransTable, translate; 13 14 import fs = std.file; 15 import io = std.stdio; 16 17 @protocolMethod("served/searchFile") 18 string[] searchFile(string query) 19 { 20 if (!query.length) 21 return null; 22 23 if (query.isAbsolute) 24 { 25 if (fs.exists(query)) 26 return [query]; 27 else 28 return null; 29 } 30 31 string[] ret; 32 string[] importFiles, importPaths; 33 importPaths = selectedWorkspace.stdlibPath(); 34 35 foreach (instance; backend.instances) 36 { 37 importFiles ~= instance.importFiles; 38 importPaths ~= instance.importPaths; 39 } 40 41 importFiles.sort!"a<b"; 42 importPaths.sort!"a<b"; 43 44 foreach (file; importFiles.uniq) 45 { 46 if (fs.exists(file) && fs.isFile(file)) 47 if (file.endsWith(query)) 48 { 49 auto rest = file[0 .. $ - query.length]; 50 if (!rest.length || rest.endsWith("/", "\\")) 51 ret ~= file; 52 } 53 } 54 foreach (dir; importPaths.uniq) 55 { 56 if (fs.exists(dir) && fs.isDir(dir)) 57 foreach (filename; fs.dirEntries(dir, fs.SpanMode.breadth)) 58 if (filename.isFile) 59 { 60 auto file = buildPath(dir, filename); 61 if (file.endsWith(query)) 62 { 63 auto rest = file[0 .. $ - query.length]; 64 if (!rest.length || rest.endsWith("/", "\\")) 65 ret ~= file; 66 } 67 } 68 } 69 70 return ret; 71 } 72 73 private string[] cachedModuleFiles; 74 private string[string] modFileCache; 75 @protocolMethod("served/findFilesByModule") 76 string[] findFilesByModule(string module_) 77 { 78 if (!module_.length) 79 return null; 80 81 if (auto cache = module_ in modFileCache) 82 return [*cache]; 83 84 scope ubyte[] buffer = new ubyte[8 * 1024]; 85 scope (exit) 86 buffer.destroy(); 87 88 string[] ret; 89 string[] importFiles, importPaths; 90 importPaths = selectedWorkspace.stdlibPath(); 91 92 foreach (instance; backend.instances) 93 { 94 importFiles ~= instance.importFiles; 95 importPaths ~= instance.importPaths; 96 } 97 98 importFiles.sort!"a<b"; 99 importPaths.sort!"a<b"; 100 101 foreach (file; importFiles.uniq) 102 { 103 if (cachedModuleFiles.binarySearch(file) >= 0) 104 continue; 105 if (fs.exists(file) && fs.isFile(file)) 106 { 107 auto fileMod = backend.get!ModulemanComponent.moduleName( 108 cast(string) file.readCodeWithBuffer(buffer)); 109 if (fileMod.startsWith("std")) 110 { 111 modFileCache[fileMod] = file; 112 cachedModuleFiles.insertSorted(file); 113 } 114 if (fileMod == module_) 115 ret ~= file; 116 } 117 } 118 foreach (dir; importPaths.uniq) 119 { 120 if (fs.exists(dir) && fs.isDir(dir)) 121 foreach (filename; fs.dirEntries(dir, fs.SpanMode.breadth)) 122 if (filename.isFile) 123 { 124 auto file = buildPath(dir, filename); 125 if (cachedModuleFiles.binarySearch(file) >= 0) 126 continue; 127 auto fileMod = moduleNameForFile(file, dir, buffer); 128 if (fileMod.startsWith("std")) 129 { 130 modFileCache[fileMod] = file; 131 cachedModuleFiles.insertSorted(file); 132 } 133 if (fileMod == module_) 134 ret ~= file; 135 } 136 } 137 138 return ret; 139 } 140 141 private string moduleNameForFile(string file, string dir, ref ubyte[] buffer) 142 { 143 auto ret = backend.get!ModulemanComponent.moduleName( 144 cast(string) file.readCodeWithBuffer(buffer)); 145 if (ret.length) 146 return ret; 147 file = buildNormalizedPath(file); 148 dir = buildNormalizedPath(dir); 149 if (file.startsWith(dir)) 150 return file[dir.length .. $].stripExtension.translate(makeTransTable("/\\", "..")); 151 else 152 return baseName(file).stripExtension; 153 }