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 if (info.lastStatement.type == "TryStatement") 66 { 67 int tryIndex = info.contextTokenIndex == 0 ? position : info.contextTokenIndex; 68 auto hasBraces = code[0 .. max(min(tryIndex, $), 0)].stripRight.endsWith("}"); 69 Snippet catchSnippet; 70 catchSnippet.providerId = typeid(this).name; 71 catchSnippet.id = "catch"; 72 catchSnippet.title = "catch"; 73 catchSnippet.shortcut = "catch"; 74 catchSnippet.documentation = "catch block"; 75 if (hasBraces) 76 { 77 catchSnippet.plain = "catch (Exception e) {\n\t\n}"; 78 catchSnippet.snippet = "catch (${1:Exception e}) {\n\t$0\n}"; 79 } 80 else 81 { 82 catchSnippet.plain = "catch (Exception e)\n\t"; 83 catchSnippet.snippet = "catch (${1:Exception e})\n\t$0"; 84 } 85 catchSnippet.unformatted = true; 86 catchSnippet.resolved = true; 87 res ~= catchSnippet; 88 89 Snippet finallySnippet; 90 finallySnippet.providerId = typeid(this).name; 91 finallySnippet.id = "finally"; 92 finallySnippet.title = "finally"; 93 finallySnippet.shortcut = "finally"; 94 finallySnippet.documentation = "finally block"; 95 if (hasBraces) 96 { 97 finallySnippet.plain = "finally {\n\t\n}"; 98 finallySnippet.snippet = "finally {\n\t$0\n}"; 99 } 100 else 101 { 102 finallySnippet.plain = "finally\n\t"; 103 finallySnippet.snippet = "finally\n\t$0"; 104 } 105 finallySnippet.unformatted = true; 106 finallySnippet.resolved = true; 107 res ~= finallySnippet; 108 } 109 110 debug (SnippetScope) 111 { 112 import served.lsp.jsonops : serializeJson; 113 114 Snippet ret; 115 ret.providerId = typeid(this).name; 116 ret.id = "workspaced-snippet-debug"; 117 ret.title = "[DEBUG] Snippet"; 118 ret.shortcut = "__debug_snippet"; 119 ret.plain = ret.snippet = info.serializeJson; 120 ret.unformatted = true; 121 ret.resolved = true; 122 res ~= ret; 123 } 124 125 return typeof(return).fromResult(res.length ? res : null); 126 } 127 128 Future!Snippet resolveSnippet(scope const WorkspaceD.Instance instance, 129 scope const(char)[] file, scope const(char)[] code, int position, 130 const SnippetInfo info, Snippet snippet) 131 { 132 return typeof(return).fromResult(snippet); 133 } 134 135 Snippet ndForeach(int n, string name = null) 136 { 137 Snippet ret; 138 ret.providerId = typeid(this).name; 139 ret.id = "nd"; 140 ret.title = "foreach over " ~ n.to!string ~ " keys"; 141 if (name.length) 142 ret.title ~= " (over " ~ name ~ ")"; 143 ret.shortcut = "foreach"; 144 ret.documentation = "Foreach over locally defined variable with " ~ n.to!string ~ " keys."; 145 string keys; 146 if (n == 2) 147 { 148 keys = "key, value"; 149 } 150 else if (n <= 4) 151 { 152 foreach (i; 0 .. n - 1) 153 { 154 keys ~= cast(char)('i' + i) ~ ", "; 155 } 156 keys ~= "value"; 157 } 158 else 159 { 160 foreach (i; 0 .. n - 1) 161 { 162 keys ~= "k" ~ (i + 1).to!string ~ ", "; 163 } 164 keys ~= "value"; 165 } 166 167 if (name.length) 168 { 169 ret.plain = "foreach (" ~ keys ~ "; " ~ name ~ ") {\n\t\n}"; 170 ret.snippet = "foreach (${1:" ~ keys ~ "}; ${2:" ~ name ~ "}) {\n\t$0\n}"; 171 } 172 else 173 { 174 ret.plain = "foreach (" ~ keys ~ "; map) {\n\t\n}"; 175 ret.snippet = "foreach (${1:" ~ keys ~ "}; ${2:map}) {\n\t$0\n}"; 176 } 177 ret.resolved = true; 178 return ret; 179 } 180 181 Snippet simpleForeach(string name = null, string type = null) 182 { 183 Snippet ret; 184 ret.providerId = typeid(this).name; 185 ret.id = "simple"; 186 ret.title = "foreach loop"; 187 if (name.length) 188 ret.title ~= " (over " ~ name ~ ")"; 189 ret.shortcut = "foreach"; 190 ret.documentation = name.length 191 ? "Foreach over locally defined variable." : "Foreach over a variable or range."; 192 string t = type.length ? type ~ " " : null; 193 if (name.length) 194 { 195 ret.plain = "foreach (" ~ t ~ "key; " ~ name ~ ") {\n\t\n}"; 196 ret.snippet = "foreach (" ~ t ~ "${1:key}; ${2:" ~ name ~ "}) {\n\t$0\n}"; 197 } 198 else 199 { 200 ret.plain = "foreach (" ~ t ~ "key; list) {\n\t\n}"; 201 ret.snippet = "foreach (" ~ t ~ "${1:key}; ${2:list}) {\n\t$0\n}"; 202 } 203 ret.resolved = true; 204 return ret; 205 } 206 207 Snippet stringIterators(string name = null) 208 { 209 Snippet ret; 210 ret.providerId = typeid(this).name; 211 ret.id = "str"; 212 ret.title = "foreach loop"; 213 if (name.length) 214 ret.title ~= " (unicode over " ~ name ~ ")"; 215 else 216 ret.title ~= " (unicode)"; 217 ret.shortcut = "foreach_utf"; 218 ret.documentation = name.length 219 ? "Foreach over locally defined variable." : "Foreach over a variable or range."; 220 if (name.length) 221 { 222 ret.plain = "foreach (char key; " ~ name ~ ") {\n\t\n}"; 223 ret.snippet = "foreach (${1|char,wchar,dchar|} ${2:key}; ${3:" ~ name ~ "}) {\n\t$0\n}"; 224 } 225 else 226 { 227 ret.plain = "foreach (char key; str) {\n\t\n}"; 228 ret.snippet = "foreach (${1|char,wchar,dchar|} ${2:key}; ${3:str}) {\n\t$0\n}"; 229 } 230 ret.resolved = true; 231 return ret; 232 } 233 }