1 module workspaced.com.snippets.smart; 2 3 // debug = SnippetScope; 4 5 import workspaced.api; 6 import workspaced.com.snippets; 7 8 import std.algorithm; 9 import std.conv; 10 import std.string; 11 12 class SmartSnippetProvider : SnippetProvider 13 { 14 Future!(Snippet[]) provideSnippets(scope const WorkspaceD.Instance instance, 15 scope const(char)[] file, scope const(char)[] code, int position, const SnippetInfo info) 16 { 17 Snippet[] res; 18 19 if (info.loopScope.supported) 20 { 21 if (info.loopScope.numItems > 1) 22 { 23 res ~= ndForeach(info.loopScope.numItems, info.loopScope.iterator); 24 res ~= simpleForeach(); 25 res ~= stringIterators(); 26 } 27 else if (info.loopScope.stringIterator) 28 { 29 res ~= simpleForeach(); 30 res ~= stringIterators(info.loopScope.iterator); 31 } 32 else 33 { 34 res ~= simpleForeach(info.loopScope.iterator, info.loopScope.type); 35 res ~= stringIterators(); 36 } 37 } 38 39 if (info.lastStatement.type == "IfStatement" 40 && !info.lastStatement.ifHasElse) 41 { 42 int ifIndex = info.contextTokenIndex == 0 ? position : info.contextTokenIndex; 43 auto hasBraces = code[0 .. max(min(ifIndex, $), 0)].stripRight.endsWith("}"); 44 Snippet snp; 45 snp.providerId = typeid(this).name; 46 snp.id = "else"; 47 snp.title = "else"; 48 snp.shortcut = "else"; 49 snp.documentation = "else block"; 50 if (hasBraces) 51 { 52 snp.plain = "else {\n\t\n}"; 53 snp.snippet = "else {\n\t$0\n}"; 54 } 55 else 56 { 57 snp.plain = "else\n\t"; 58 snp.snippet = "else\n\t$0"; 59 } 60 snp.unformatted = true; 61 snp.resolved = true; 62 res ~= snp; 63 } 64 65 debug (SnippetScope) 66 { 67 import painlessjson : toJSON; 68 69 Snippet ret; 70 ret.providerId = typeid(this).name; 71 ret.id = "workspaced-snippet-debug"; 72 ret.title = "[DEBUG] Snippet"; 73 ret.shortcut = "__debug_snippet"; 74 ret.plain = ret.snippet = info.toJSON.toPrettyString; 75 ret.unformatted = true; 76 ret.resolved = true; 77 res ~= ret; 78 } 79 80 return typeof(return).fromResult(res.length ? res : null); 81 } 82 83 Future!Snippet resolveSnippet(scope const WorkspaceD.Instance instance, 84 scope const(char)[] file, scope const(char)[] code, int position, 85 const SnippetInfo info, Snippet snippet) 86 { 87 return typeof(return).fromResult(snippet); 88 } 89 90 Snippet ndForeach(int n, string name = null) 91 { 92 Snippet ret; 93 ret.providerId = typeid(this).name; 94 ret.id = "nd"; 95 ret.title = "foreach over " ~ n.to!string ~ " keys"; 96 if (name.length) 97 ret.title ~= " (over " ~ name ~ ")"; 98 ret.shortcut = "foreach"; 99 ret.documentation = "Foreach over locally defined variable with " ~ n.to!string ~ " keys."; 100 string keys; 101 if (n == 2) 102 { 103 keys = "key, value"; 104 } 105 else if (n <= 4) 106 { 107 foreach (i; 0 .. n - 1) 108 { 109 keys ~= cast(char)('i' + i) ~ ", "; 110 } 111 keys ~= "value"; 112 } 113 else 114 { 115 foreach (i; 0 .. n - 1) 116 { 117 keys ~= "k" ~ (i + 1).to!string ~ ", "; 118 } 119 keys ~= "value"; 120 } 121 122 if (name.length) 123 { 124 ret.plain = "foreach (" ~ keys ~ "; " ~ name ~ ") {\n\t\n}"; 125 ret.snippet = "foreach (${1:" ~ keys ~ "}; ${2:" ~ name ~ "}) {\n\t$0\n}"; 126 } 127 else 128 { 129 ret.plain = "foreach (" ~ keys ~ "; map) {\n\t\n}"; 130 ret.snippet = "foreach (${1:" ~ keys ~ "}; ${2:map}) {\n\t$0\n}"; 131 } 132 ret.resolved = true; 133 return ret; 134 } 135 136 Snippet simpleForeach(string name = null, string type = null) 137 { 138 Snippet ret; 139 ret.providerId = typeid(this).name; 140 ret.id = "simple"; 141 ret.title = "foreach loop"; 142 if (name.length) 143 ret.title ~= " (over " ~ name ~ ")"; 144 ret.shortcut = "foreach"; 145 ret.documentation = name.length 146 ? "Foreach over locally defined variable." : "Foreach over a variable or range."; 147 string t = type.length ? type ~ " " : null; 148 if (name.length) 149 { 150 ret.plain = "foreach (" ~ t ~ "key; " ~ name ~ ") {\n\t\n}"; 151 ret.snippet = "foreach (" ~ t ~ "${1:key}; ${2:" ~ name ~ "}) {\n\t$0\n}"; 152 } 153 else 154 { 155 ret.plain = "foreach (" ~ t ~ "key; list) {\n\t\n}"; 156 ret.snippet = "foreach (" ~ t ~ "${1:key}; ${2:list}) {\n\t$0\n}"; 157 } 158 ret.resolved = true; 159 return ret; 160 } 161 162 Snippet stringIterators(string name = null) 163 { 164 Snippet ret; 165 ret.providerId = typeid(this).name; 166 ret.id = "str"; 167 ret.title = "foreach loop"; 168 if (name.length) 169 ret.title ~= " (unicode over " ~ name ~ ")"; 170 else 171 ret.title ~= " (unicode)"; 172 ret.shortcut = "foreach_utf"; 173 ret.documentation = name.length 174 ? "Foreach over locally defined variable." : "Foreach over a variable or range."; 175 if (name.length) 176 { 177 ret.plain = "foreach (char key; " ~ name ~ ") {\n\t\n}"; 178 ret.snippet = "foreach (${1|char,wchar,dchar|} ${2:key}; ${3:" ~ name ~ "}) {\n\t$0\n}"; 179 } 180 else 181 { 182 ret.plain = "foreach (char key; str) {\n\t\n}"; 183 ret.snippet = "foreach (${1|char,wchar,dchar|} ${2:key}; ${3:str}) {\n\t$0\n}"; 184 } 185 ret.resolved = true; 186 return ret; 187 } 188 }