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