1 module served.utils.diet; 2 3 import dietc.complete; 4 import dietc.lexer; 5 import dietc.parser; 6 7 import vscode = served.lsp.protocol; 8 9 import ext = served.extension; 10 import served.types : documents; 11 12 import std.algorithm; 13 import std.experimental.logger; 14 import std.path; 15 import std.string; 16 17 DietComplete[string] dietFileCache; 18 19 DietComplete updateDietFile(string file, string content) 20 { 21 if (auto existing = file in dietFileCache) 22 { 23 existing.reparse(content); 24 return *existing; 25 } 26 else 27 { 28 DietInput input; 29 input.file = file; 30 input.code = content; 31 auto ret = new DietComplete(input, DietComplete.defaultFileProvider(file.dirName)); 32 dietFileCache[file] = ret; 33 return ret; 34 } 35 } 36 37 vscode.CompletionItemKind mapToCompletionItemKind(CompletionType type) 38 { 39 final switch (type) 40 { 41 case CompletionType.none: 42 return vscode.CompletionItemKind.text; 43 case CompletionType.tag: 44 return vscode.CompletionItemKind.keyword; 45 case CompletionType.attribute: 46 return vscode.CompletionItemKind.property; 47 case CompletionType.value: 48 return vscode.CompletionItemKind.constant; 49 case CompletionType.reference: 50 return vscode.CompletionItemKind.reference; 51 case CompletionType.cssName: 52 return vscode.CompletionItemKind.property; 53 case CompletionType.cssValue: 54 return vscode.CompletionItemKind.value; 55 case CompletionType.d: 56 return vscode.CompletionItemKind.snippet; 57 case CompletionType.meta: 58 return vscode.CompletionItemKind.keyword; 59 } 60 } 61 62 void contextExtractD(DietComplete completion, size_t offset, out string code, 63 out size_t dOffset, bool extractContext) 64 { 65 string prefix; 66 if (completion.parser.root.children.length > 0) 67 { 68 int i = 0; 69 if (auto node = cast(TagNode) completion.parser.root.children[i]) 70 { 71 if (node.name == "extends" && completion.parser.root.children.length > 1) 72 i++; 73 } 74 75 if (auto comment = cast(HiddenComment) completion.parser.root.children[i]) 76 { 77 string startComment = comment.content.strip; 78 info("Have context ", startComment); 79 if (startComment.startsWith("context=") && extractContext) 80 { 81 auto context = startComment["context=".length .. $]; 82 auto end = context.indexOfAny(" ;,"); 83 if (end != -1) 84 context = context[0 .. end]; 85 86 context = context.strip; 87 if (!context.endsWith(".d")) 88 context ~= ".d"; 89 90 auto currentFile = completion.parser.input.file.baseName; 91 92 foreach (doc; documents.documentStore) 93 { 94 if (doc.uri.endsWith(context)) 95 { 96 auto content = doc.rawText; 97 infof("Searching for diet file '%s' in context file '%s'", currentFile, doc.uri); 98 99 auto index = searchDietTemplateUsage(content, currentFile); 100 if (index >= 0) 101 prefix = content[0 .. index].idup; 102 else 103 info("Failed finding diet file, error ", index); 104 break; 105 } 106 } 107 108 if (!prefix.length) 109 { 110 infof("Didn't find any valid context for diet file '%s' when searching for context '%s'", 111 currentFile, context); 112 } 113 } 114 } 115 } 116 117 extractD(completion, offset, code, dOffset, prefix); 118 } 119 120 ptrdiff_t searchDietTemplateUsage(scope const(char)[] code, scope const(char)[] dietFile) 121 { 122 auto index = code.indexOf(dietFile); 123 if (index == -1) 124 return -2; 125 126 if (!code[index + dietFile.length .. $].startsWith("\"", "`")) 127 return -3; 128 129 auto funcStart = code[0 .. index].lastIndexOfAny(";!("); 130 if (funcStart == -1) 131 return -4; 132 133 return code[0 .. funcStart].lastIndexOfAny(";\r\n"); 134 }