1 module served.commands.code_lens; 2 3 import served.commands.code_actions; 4 5 import served.extension; 6 import served.types; 7 import served.utils.fibermanager; 8 9 import workspaced.api; 10 import workspaced.coms; 11 12 import core.time : minutes, msecs; 13 14 import std.algorithm : startsWith; 15 import std.conv : to; 16 import std.datetime.stopwatch : StopWatch; 17 import std.datetime.systime : Clock, SysTime; 18 import std.regex : matchAll; 19 20 @protocolMethod("textDocument/codeLens") 21 CodeLens[] provideCodeLens(CodeLensParams params) 22 { 23 auto document = documents[params.textDocument.uri]; 24 string file = document.uri.uriToFile; 25 if (document.languageId != "d") 26 return []; 27 CodeLens[] ret; 28 if (workspace(params.textDocument.uri).config.d.enableDMDImportTiming) 29 { 30 size_t lastIndex = size_t.max; 31 Position lastPosition; 32 33 foreach (match; document.rawText.matchAll(importRegex)) 34 { 35 size_t index = match.pre.length; 36 auto pos = document.movePositionBytes(lastPosition, lastIndex, index); 37 lastIndex = index; 38 lastPosition = pos; 39 40 ret ~= CodeLens(TextRange(pos), Optional!Command.init, 41 JsonValue([ 42 "type": JsonValue("importcompilecheck"), 43 "code": JsonValue(match.hit.idup), 44 "module": JsonValue(match[1].idup), 45 "file": JsonValue(file) 46 ]).opt); 47 } 48 } 49 return ret; 50 } 51 52 @protocolMethod("codeLens/resolve") 53 CodeLens resolveCodeLens(CodeLens lens) 54 { 55 if (lens.data.isNone || 56 lens.data.deref.kind != JsonValue.Kind.object) 57 throw new Exception("Invalid Lens Object"); 58 59 auto lensData = lens.data.deref.get!(StringMap!JsonValue); 60 auto type = lensData.get("type", JsonValue("")) 61 .match!((string s) => s, _ => ""); 62 switch (type) 63 { 64 case "importcompilecheck": 65 try 66 { 67 auto code = lensData.get("code", JsonValue(null)) 68 .match!((string s) => s, _ => ""); 69 if (!code.length) 70 throw new Exception("No valid code provided"); 71 72 auto module_ = lensData.get("module", JsonValue(null)) 73 .match!((string s) => s, _ => ""); 74 if (!module_.length) 75 throw new Exception("No valid module provided"); 76 77 auto file = lensData.get("file", JsonValue(null)) 78 .match!((string s) => s, _ => ""); 79 if (!file.length) 80 throw new Exception("No valid file provided"); 81 82 int decMs = getImportCompilationTime(code, module_, file); 83 lens.command = Command((decMs < 10 84 ? "no noticable effect" : "~" ~ decMs.to!string ~ "ms") ~ " for importing this"); 85 return lens; 86 } 87 catch (Exception) 88 { 89 lens.command = Command.init; 90 return lens; 91 } 92 default: 93 throw new Exception("Unknown lens type"); 94 } 95 } 96 97 bool importCompilationTimeRunning; 98 int getImportCompilationTime(string code, string module_, string file) 99 { 100 import std.math : round; 101 102 static struct CompileCache 103 { 104 SysTime at; 105 string code; 106 int ret; 107 } 108 109 static CompileCache[] cache; 110 111 auto now = Clock.currTime; 112 113 foreach_reverse (i, exist; cache) 114 { 115 if (exist.code != code) 116 continue; 117 if (now - exist.at < (exist.ret >= 500 ? 20.minutes : exist.ret >= 30 ? 5.minutes 118 : 2.minutes) || module_.startsWith("std.")) 119 return exist.ret; 120 else 121 { 122 cache[i] = cache[$ - 1]; 123 cache.length--; 124 } 125 } 126 127 while (importCompilationTimeRunning) 128 Fiber.yield(); 129 importCompilationTimeRunning = true; 130 scope (exit) 131 importCompilationTimeRunning = false; 132 // run blocking so we don't compute multiple in parallel 133 auto ret = backend.best!DMDComponent(file).measureSync(code, null, 20, 500); 134 if (!ret.success) 135 throw new Exception("Compilation failed"); 136 auto msecs = cast(int) round(ret.duration.total!"msecs" / 5.0) * 5; 137 cache ~= CompileCache(now, code, msecs); 138 StopWatch sw; 139 sw.start(); 140 while (sw.peek < 100.msecs) // pass through requests for 100ms 141 Fiber.yield(); 142 return msecs; 143 }