1 module served.lsp.protocol; 2 3 import std.conv; 4 import std.json; 5 import std.meta; 6 import std.traits; 7 8 import painlessjson; 9 10 struct Optional(T) 11 { 12 bool isNull = true; 13 T value; 14 15 this(T val) 16 { 17 value = val; 18 isNull = false; 19 } 20 21 this(U)(U val) 22 { 23 value = val; 24 isNull = false; 25 } 26 27 this(typeof(null)) 28 { 29 isNull = true; 30 } 31 32 void opAssign(typeof(null)) 33 { 34 nullify(); 35 } 36 37 void opAssign(T val) 38 { 39 isNull = false; 40 value = val; 41 } 42 43 void opAssign(U)(U val) 44 { 45 isNull = false; 46 value = val; 47 } 48 49 void nullify() 50 { 51 isNull = true; 52 value = T.init; 53 } 54 55 string toString() const 56 { 57 if (isNull) 58 return "null(" ~ T.stringof ~ ")"; 59 else 60 return value.to!string; 61 } 62 63 const JSONValue _toJSON() 64 { 65 import painlessjson : toJSON; 66 67 if (isNull) 68 return JSONValue(null); 69 else 70 return value.toJSON; 71 } 72 73 static Optional!T _fromJSON(JSONValue val) 74 { 75 Optional!T ret; 76 ret.isNull = false; 77 ret.value = val.fromJSON!T; 78 return ret; 79 } 80 81 ref inout(T) get() inout 82 { 83 return value; 84 } 85 86 alias value this; 87 } 88 89 mixin template StrictOptionalSerializer() 90 { 91 const JSONValue _toJSON() 92 { 93 JSONValue[string] ret = this.defaultToJSON.object; 94 foreach (member; __traits(allMembers, typeof(this))) 95 static if (is(typeof(__traits(getMember, this, member)) == Optional!T, T)) 96 { 97 static if (hasUDA!(__traits(getMember, this, member), SerializedName)) 98 string name = getUDAs!(__traits(getMember, this, member), SerializedName)[0].to; 99 else static if (hasUDA!(__traits(getMember, this, member), SerializedToName)) 100 string name = getUDAs!(__traits(getMember, this, member), SerializedToName)[0].name; 101 else 102 string name = member; 103 104 if (__traits(getMember, this, member).isNull) 105 ret.remove(name); 106 } 107 return JSONValue(ret); 108 } 109 } 110 111 Optional!T opt(T)(T val) 112 { 113 return Optional!T(val); 114 } 115 116 struct ArrayOrSingle(T) 117 { 118 T[] value; 119 120 this(T val) 121 { 122 value = [val]; 123 } 124 125 this(T[] val) 126 { 127 value = val; 128 } 129 130 void opAssign(T val) 131 { 132 value = [val]; 133 } 134 135 void opAssign(T[] val) 136 { 137 value = val; 138 } 139 140 const JSONValue _toJSON() 141 { 142 if (value.length == 1) 143 return value[0].toJSON; 144 else 145 return value.toJSON; 146 } 147 148 static ArrayOrSingle!T _fromJSON(JSONValue val) 149 { 150 ArrayOrSingle!T ret; 151 if (val.type == JSONType.array) 152 ret.value = val.fromJSON!(T[]); 153 else 154 ret.value = [val.fromJSON!T]; 155 return ret; 156 } 157 158 alias value this; 159 } 160 161 static assert(__traits(compiles, ArrayOrSingle!Location.init._toJSON())); 162 static assert(__traits(compiles, ArrayOrSingle!Location._fromJSON(JSONValue.init))); 163 164 unittest 165 { 166 auto single = ArrayOrSingle!Location(Location("file:///foo.d", TextRange(4, 2, 4, 8))); 167 auto array = ArrayOrSingle!Location([Location("file:///foo.d", TextRange(4, 2, 4, 8)), Location("file:///bar.d", TextRange(14, 1, 14, 9))]); 168 assert(toJSON(single) == JSONValue([ 169 "range": JSONValue([ 170 "start": JSONValue(["line":JSONValue(4), "character":JSONValue(2)]), 171 "end": JSONValue(["line":JSONValue(4), "character":JSONValue(8)]) 172 ]), 173 "uri": JSONValue("file:///foo.d") 174 ])); 175 assert(toJSON(array) == JSONValue([ 176 JSONValue([ 177 "range": JSONValue([ 178 "start": JSONValue(["line":JSONValue(4), "character":JSONValue(2)]), 179 "end": JSONValue(["line":JSONValue(4), "character":JSONValue(8)]) 180 ]), 181 "uri": JSONValue("file:///foo.d") 182 ]), 183 JSONValue([ 184 "range": JSONValue([ 185 "start": JSONValue(["line":JSONValue(14), "character":JSONValue(1)]), 186 "end": JSONValue(["line":JSONValue(14), "character":JSONValue(9)]) 187 ]), 188 "uri": JSONValue("file:///bar.d") 189 ]) 190 ])); 191 assert(fromJSON!(ArrayOrSingle!Location)(toJSON(single)) == single, fromJSON!(ArrayOrSingle!Location)(toJSON(single)).value.to!string); 192 assert(fromJSON!(ArrayOrSingle!Location)(toJSON(array)) == array); 193 } 194 195 struct RequestToken 196 { 197 this(const(JSONValue)* val) 198 { 199 if (!val) 200 { 201 hasData = false; 202 return; 203 } 204 hasData = true; 205 if (val.type == JSONType..string) 206 { 207 isString = true; 208 str = val.str; 209 } 210 else if (val.type == JSONType.integer) 211 { 212 isString = false; 213 num = val.integer; 214 } 215 else 216 throw new Exception("Invalid ID"); 217 } 218 219 union 220 { 221 string str; 222 long num; 223 } 224 225 bool hasData, isString; 226 227 JSONValue toJSON() 228 { 229 JSONValue ret = null; 230 if (!hasData) 231 return ret; 232 ret = isString ? JSONValue(str) : JSONValue(num); 233 return ret; 234 } 235 236 JSONValue _toJSON()() 237 { 238 pragma(msg, "Attempted painlesstraits.toJSON on RequestToken"); 239 } 240 241 void _fromJSON()(JSONValue val) 242 { 243 pragma(msg, "Attempted painlesstraits.fromJSON on RequestToken"); 244 } 245 246 string toString() 247 { 248 return hasData ? (isString ? str : num.to!string) : "none"; 249 } 250 251 static RequestToken random() 252 { 253 import std.uuid; 254 255 JSONValue id = JSONValue(randomUUID.toString); 256 return RequestToken(&id); 257 } 258 259 bool opEquals(RequestToken b) const 260 { 261 return isString == b.isString && (isString ? str == b.str : num == b.num); 262 } 263 } 264 265 struct RequestMessage 266 { 267 this(JSONValue val) 268 { 269 id = RequestToken("id" in val); 270 method = val["method"].str; 271 auto ptr = "params" in val; 272 if (ptr) 273 params = *ptr; 274 } 275 276 RequestToken id; 277 string method; 278 JSONValue params; 279 280 bool isCancelRequest() 281 { 282 return method == "$/cancelRequest"; 283 } 284 285 JSONValue toJSON() 286 { 287 auto ret = JSONValue([ 288 "jsonrpc": JSONValue("2.0"), 289 "method": JSONValue(method) 290 ]); 291 if (!params.isNull) 292 ret["params"] = params; 293 if (id.hasData) 294 ret["id"] = id.toJSON; 295 return ret; 296 } 297 } 298 299 enum ErrorCode 300 { 301 parseError = -32700, 302 invalidRequest = -32600, 303 methodNotFound = -32601, 304 invalidParams = -32602, 305 internalError = -32603, 306 serverErrorStart = -32099, 307 serverErrorEnd = -32000, 308 serverNotInitialized = -32002, 309 unknownErrorCode = -32001 310 } 311 312 enum MessageType 313 { 314 error = 1, 315 warning, 316 info, 317 log 318 } 319 320 struct ResponseError 321 { 322 ErrorCode code; 323 string message; 324 JSONValue data; 325 326 this(Throwable t) 327 { 328 code = ErrorCode.unknownErrorCode; 329 message = t.msg; 330 data = JSONValue(t.to!string); 331 } 332 333 this(ErrorCode c) 334 { 335 code = c; 336 message = c.to!string; 337 } 338 339 this(ErrorCode c, string msg) 340 { 341 code = c; 342 message = msg; 343 } 344 } 345 346 class MethodException : Exception 347 { 348 this(ResponseError error, string file = __FILE__, size_t line = __LINE__) pure nothrow @nogc @safe 349 { 350 super(error.message, file, line); 351 this.error = error; 352 } 353 354 ResponseError error; 355 } 356 357 struct ResponseMessage 358 { 359 this(RequestToken id, JSONValue result) 360 { 361 this.id = id; 362 this.result = result; 363 } 364 365 this(RequestToken id, ResponseError error) 366 { 367 this.id = id; 368 this.error = error; 369 } 370 371 RequestToken id; 372 Optional!JSONValue result; 373 Optional!ResponseError error; 374 } 375 376 alias DocumentUri = string; 377 378 enum EolType 379 { 380 cr, 381 lf, 382 crlf 383 } 384 385 string toString(EolType eol) 386 { 387 final switch (eol) 388 { 389 case EolType.cr: 390 return "\r"; 391 case EolType.lf: 392 return "\n"; 393 case EolType.crlf: 394 return "\r\n"; 395 } 396 } 397 398 struct Position 399 { 400 /// Zero-based line & character offset (UTF-16 codepoints) 401 uint line, character; 402 403 int opCmp(const Position other) const 404 { 405 if (line < other.line) 406 return -1; 407 if (line > other.line) 408 return 1; 409 if (character < other.character) 410 return -1; 411 if (character > other.character) 412 return 1; 413 return 0; 414 } 415 416 const JSONValue _toJSON() 417 { 418 return JSONValue(["line": JSONValue(line), "character": JSONValue(character)]); 419 } 420 421 static Position _fromJSON(const JSONValue val) 422 { 423 import std.exception : enforce; 424 425 enforce(val.type == JSONType.object); 426 auto line = val.object.get("line", JSONValue.init); 427 auto character = val.object.get("character", JSONValue.init); 428 429 uint iline, icharacter; 430 431 if (line.type == JSONType.integer) 432 iline = cast(uint)line.integer; 433 else if (line.type == JSONType.uinteger) 434 iline = cast(uint)line.uinteger; 435 else 436 throw new JSONException("Position['line'] is not an integer"); 437 438 if (character.type == JSONType.integer) 439 icharacter = cast(uint)character.integer; 440 else if (character.type == JSONType.uinteger) 441 icharacter = cast(uint)character.uinteger; 442 else 443 throw new JSONException("Position['character'] is not an integer"); 444 445 return Position(iline, icharacter); 446 } 447 } 448 449 static assert(__traits(compiles, Position.init._toJSON())); 450 static assert(__traits(compiles, Position._fromJSON(JSONValue.init))); 451 452 struct TextRange 453 { 454 union 455 { 456 struct 457 { 458 Position start; 459 Position end; 460 } 461 462 Position[2] range; 463 } 464 465 enum all = TextRange(0, 0, int.max, int.max); // int.max ought to be enough 466 467 alias range this; 468 469 this(Num)(Num startLine, Num startCol, Num endLine, Num endCol) if (isNumeric!Num) 470 { 471 this(Position(cast(uint) startLine, cast(uint) startCol), 472 Position(cast(uint) endLine, cast(uint) endCol)); 473 } 474 475 this(Position start, Position end) 476 { 477 this.start = start; 478 this.end = end; 479 } 480 481 this(Position[2] range) 482 { 483 this.range = range; 484 } 485 486 this(Position pos) 487 { 488 this.start = pos; 489 this.end = pos; 490 } 491 492 /// Returns: true if this range contains the position or the position is at 493 /// the edges of this range. 494 bool contains(Position position) 495 { 496 int minLine = start.line; 497 int minCol = start.character; 498 int maxLine = end.line; 499 int maxCol = end.character; 500 501 return !(position.line < minLine || position.line > maxLine 502 || (position.line == minLine && position.character < minCol) 503 || (position.line == maxLine && position.character > maxCol)); 504 } 505 506 /// Returns: true if text range `a` and `b` intersect with at least one character. 507 /// This function is commutative (a·b == b·a) 508 bool intersects(const TextRange b) 509 { 510 return start < b.end && end > b.start; 511 } 512 513 /// 514 unittest 515 { 516 bool test(TextRange a, TextRange b) 517 { 518 bool res = a.intersects(b); 519 // test commutativity 520 assert(res == b.intersects(a)); 521 return res; 522 } 523 524 assert(test(TextRange(10, 4, 20, 3), TextRange(20, 2, 30, 1))); 525 assert(!test(TextRange(10, 4, 20, 3), TextRange(20, 3, 30, 1))); 526 assert(test(TextRange(10, 4, 20, 3), TextRange(12, 3, 14, 1))); 527 assert(!test(TextRange(10, 4, 20, 3), TextRange(9, 3, 10, 4))); 528 assert(test(TextRange(10, 4, 20, 3), TextRange(9, 3, 10, 5))); 529 assert(test(TextRange(10, 4, 20, 3), TextRange(10, 4, 20, 3))); 530 assert(test(TextRange(0, 0, 0, 1), TextRange(0, 0, uint.max, uint.max))); 531 assert(!test(TextRange(0, 0, 0, 1), TextRange(uint.max, uint.max, uint.max, uint.max))); 532 } 533 534 const JSONValue _toJSON() 535 { 536 import painlessjson : toJSON; 537 538 return JSONValue([ 539 "start": start.toJSON, 540 "end": end.toJSON 541 ]); 542 } 543 544 static TextRange _fromJSON(const JSONValue val) 545 { 546 import painlessjson : fromJSON; 547 548 return TextRange(val.object["start"].fromJSON!Position, val.object["end"].fromJSON!Position); 549 } 550 } 551 552 static assert(__traits(compiles, TextRange.init._toJSON())); 553 static assert(__traits(compiles, TextRange._fromJSON(JSONValue.init))); 554 555 struct Location 556 { 557 DocumentUri uri; 558 TextRange range; 559 } 560 561 struct Diagnostic 562 { 563 mixin StrictOptionalSerializer; 564 565 TextRange range; 566 Optional!DiagnosticSeverity severity; 567 Optional!JSONValue code; 568 Optional!string source; 569 string message; 570 Optional!(DiagnosticRelatedInformation[]) relatedInformation; 571 Optional!(DiagnosticTag[]) tags; 572 } 573 574 struct DiagnosticRelatedInformation 575 { 576 Location location; 577 string message; 578 } 579 580 enum DiagnosticSeverity 581 { 582 error = 1, 583 warning, 584 information, 585 hint 586 } 587 588 enum DiagnosticTag 589 { 590 unnecessary = 1, 591 deprecated_ 592 } 593 594 struct Command 595 { 596 string title; 597 string command; 598 JSONValue[] arguments; 599 } 600 601 struct TextEdit 602 { 603 TextRange range; 604 string newText; 605 606 this(TextRange range, string newText) 607 { 608 this.range = range; 609 this.newText = newText; 610 } 611 612 this(Position[2] range, string newText) 613 { 614 this.range = TextRange(range); 615 this.newText = newText; 616 } 617 618 const JSONValue _toJSON() 619 { 620 return JSONValue(["range": range._toJSON, "newText": JSONValue(newText)]); 621 } 622 623 static TextEdit _fromJSON(const JSONValue val) 624 { 625 TextEdit ret; 626 ret.range = TextRange._fromJSON(val["range"]); 627 ret.newText = val["newText"].str; 628 return ret; 629 } 630 } 631 632 static assert(__traits(compiles, TextEdit.init._toJSON())); 633 static assert(__traits(compiles, TextEdit._fromJSON(JSONValue.init))); 634 635 unittest 636 { 637 assert(toJSON(TextEdit([Position(0, 0), Position(4, 4)], "hello\nworld!")) == JSONValue([ 638 "range": JSONValue([ 639 "start": JSONValue(["line":JSONValue(0), "character":JSONValue(0)]), 640 "end": JSONValue(["line":JSONValue(4), "character":JSONValue(4)]) 641 ]), 642 "newText": JSONValue("hello\nworld!") 643 ])); 644 assert(fromJSON!TextEdit(toJSON(TextEdit([Position(0, 0), Position(4, 4)], "hello\nworld!"))) == TextEdit([Position(0, 0), Position(4, 4)], "hello\nworld!")); 645 } 646 647 struct CreateFileOptions 648 { 649 mixin StrictOptionalSerializer; 650 651 Optional!bool overwrite; 652 Optional!bool ignoreIfExists; 653 } 654 655 struct CreateFile 656 { 657 mixin StrictOptionalSerializer; 658 659 string uri; 660 Optional!CreateFileOptions options; 661 string kind = "create"; 662 } 663 664 struct RenameFileOptions 665 { 666 mixin StrictOptionalSerializer; 667 668 Optional!bool overwrite; 669 Optional!bool ignoreIfExists; 670 } 671 672 struct RenameFile 673 { 674 mixin StrictOptionalSerializer; 675 676 string oldUri; 677 string newUri; 678 Optional!RenameFileOptions options; 679 string kind = "rename"; 680 } 681 682 struct DeleteFileOptions 683 { 684 mixin StrictOptionalSerializer; 685 686 Optional!bool recursive; 687 Optional!bool ignoreIfNotExists; 688 } 689 690 struct DeleteFile 691 { 692 mixin StrictOptionalSerializer; 693 694 string uri; 695 Optional!DeleteFileOptions options; 696 string kind = "delete"; 697 } 698 699 struct TextDocumentEdit 700 { 701 VersionedTextDocumentIdentifier textDocument; 702 TextEdit[] edits; 703 } 704 705 alias TextEditCollection = TextEdit[]; 706 707 struct WorkspaceEdit 708 { 709 mixin StrictOptionalSerializer; 710 711 TextEditCollection[DocumentUri] changes; 712 713 Optional!JSONValue documentChanges; 714 } 715 716 struct TextDocumentIdentifier 717 { 718 DocumentUri uri; 719 } 720 721 struct VersionedTextDocumentIdentifier 722 { 723 DocumentUri uri; 724 @SerializedName("version") long version_; 725 } 726 727 struct TextDocumentItem 728 { 729 DocumentUri uri; 730 string languageId; 731 @SerializedName("version") long version_; 732 string text; 733 } 734 735 struct TextDocumentPositionParams 736 { 737 TextDocumentIdentifier textDocument; 738 Position position; 739 } 740 741 struct DocumentFilter 742 { 743 Optional!string language; 744 Optional!string scheme; 745 Optional!string pattern; 746 } 747 748 alias DocumentSelector = DocumentFilter[]; 749 750 struct InitializeParams 751 { 752 int processId; 753 string rootPath; 754 DocumentUri rootUri; 755 JSONValue initializationOptions; 756 ClientCapabilities capabilities; 757 string trace = "off"; 758 WorkspaceFolder[] workspaceFolders; 759 } 760 761 struct DynamicRegistration 762 { 763 mixin StrictOptionalSerializer; 764 765 Optional!bool dynamicRegistration; 766 } 767 768 enum ResourceOperationKind : string 769 { 770 create = "create", 771 rename = "rename", 772 delete_ = "delete" 773 } 774 775 enum FailureHandlingKind : string 776 { 777 abort = "abort", 778 transactional = "transactional", 779 textOnlyTransactional = "textOnlyTransactional", 780 undo = "undo" 781 } 782 783 struct WorkspaceEditClientCapabilities 784 { 785 mixin StrictOptionalSerializer; 786 787 Optional!bool documentChanges; 788 Optional!(string[]) resourceOperations; 789 Optional!string failureHandling; 790 } 791 792 struct WorkspaceClientCapabilities 793 { 794 mixin StrictOptionalSerializer; 795 796 bool applyEdit; 797 Optional!WorkspaceEditClientCapabilities workspaceEdit; 798 Optional!DynamicRegistration didChangeConfiguration; 799 Optional!DynamicRegistration didChangeWatchedFiles; 800 Optional!DynamicRegistration symbol; 801 Optional!DynamicRegistration executeCommand; 802 Optional!bool workspaceFolders; 803 Optional!bool configuration; 804 } 805 806 struct TextDocumentClientCapabilities 807 { 808 mixin StrictOptionalSerializer; 809 810 struct SyncInfo 811 { 812 mixin StrictOptionalSerializer; 813 814 Optional!bool dynamicRegistration; 815 Optional!bool willSave; 816 Optional!bool willSaveWaitUntil; 817 Optional!bool didSave; 818 } 819 820 struct CompletionInfo 821 { 822 mixin StrictOptionalSerializer; 823 824 struct CompletionItem 825 { 826 mixin StrictOptionalSerializer; 827 828 Optional!bool snippetSupport; 829 Optional!bool commitCharactersSupport; 830 //Optional!(MarkupKind[]) documentationFormat; 831 Optional!bool deprecatedSupport; 832 Optional!bool preselectSupport; 833 } 834 835 struct CompletionItemKindSet 836 { 837 mixin StrictOptionalSerializer; 838 839 // CompletionItemKind[] 840 Optional!(int[]) valueSet; 841 } 842 843 Optional!bool dynamicRegistration; 844 Optional!CompletionItem completionItem; 845 Optional!CompletionItemKindSet completionItemKind; 846 Optional!bool contextSupport; 847 } 848 849 struct SignatureHelpInfo 850 { 851 struct SignatureInformationInfo 852 { 853 struct ParameterInformationInfo 854 { 855 mixin StrictOptionalSerializer; 856 857 Optional!bool labelOffsetSupport; 858 } 859 860 mixin StrictOptionalSerializer; 861 862 // MarkupKind[] 863 Optional!(string[]) documentationFormat; 864 Optional!ParameterInformationInfo parameterInformation; 865 } 866 867 mixin StrictOptionalSerializer; 868 869 Optional!bool dynamicRegistration; 870 Optional!SignatureInformationInfo signatureInformation; 871 872 @SerializeIgnore bool supportsLabelOffset() @property 873 { 874 if (signatureInformation.isNull || signatureInformation.parameterInformation.isNull 875 || signatureInformation.parameterInformation.labelOffsetSupport.isNull) 876 return false; 877 return signatureInformation.parameterInformation.labelOffsetSupport.get; 878 } 879 } 880 881 struct DocumentSymbolInfo 882 { 883 mixin StrictOptionalSerializer; 884 885 struct SymbolKindSet 886 { 887 mixin StrictOptionalSerializer; 888 889 // SymbolKind[] 890 Optional!(int[]) valueSet; 891 } 892 893 Optional!bool dynamicRegistration; 894 Optional!SymbolKindSet symbolKind; 895 Optional!bool hierarchicalDocumentSymbolSupport; 896 } 897 898 struct PublishDiagnosticsCap 899 { 900 mixin StrictOptionalSerializer; 901 902 Optional!bool relatedInformation; 903 } 904 905 struct CodeActionClientCapabilities 906 { 907 struct CodeActionLiteralSupport 908 { 909 struct CodeActionKinds 910 { 911 // CodeActionKind[] 912 string[] valueSet; 913 } 914 915 CodeActionKinds codeActionKind; 916 } 917 918 mixin StrictOptionalSerializer; 919 920 Optional!bool dynamicRegistration; 921 Optional!CodeActionLiteralSupport codeActionLiteralSupport; 922 } 923 924 Optional!SyncInfo synchronization; 925 Optional!CompletionInfo completion; 926 Optional!DynamicRegistration hover; 927 Optional!SignatureHelpInfo signatureHelp; 928 Optional!DynamicRegistration references; 929 Optional!DynamicRegistration documentHighlight; 930 Optional!DocumentSymbolInfo documentSymbol; 931 Optional!DynamicRegistration formatting; 932 Optional!DynamicRegistration rangeFormatting; 933 Optional!DynamicRegistration onTypeFormatting; 934 Optional!DynamicRegistration definition; 935 Optional!DynamicRegistration typeDefinition; 936 Optional!DynamicRegistration implementation; 937 Optional!CodeActionClientCapabilities codeAction; 938 Optional!DynamicRegistration codeLens; 939 Optional!DynamicRegistration documentLink; 940 Optional!DynamicRegistration colorProvider; 941 Optional!DynamicRegistration rename; 942 Optional!PublishDiagnosticsCap publishDiagnostics; 943 } 944 945 enum CodeActionKind : string 946 { 947 empty = "", 948 quickfix = "quickfix", 949 refactor = "refactor", 950 refactorExtract = "refactor.extract", 951 refactorInline = "refactor.inline", 952 refactorRewrite = "refactor.rewrite", 953 refactorSource = "source", 954 sourceOrganizeImports = "source.organizeImports", 955 } 956 957 struct CodeAction 958 { 959 mixin StrictOptionalSerializer; 960 961 this(Command command) 962 { 963 title = command.title; 964 this.command = command; 965 } 966 967 this(string title, WorkspaceEdit edit) 968 { 969 this.title = title; 970 this.edit = edit; 971 } 972 973 string title; 974 // CodeActionKind 975 Optional!string kind; 976 Optional!(Diagnostic[]) diagnostics; 977 Optional!bool isPreferred; 978 Optional!WorkspaceEdit edit; 979 Optional!Command command; 980 } 981 982 struct ClientCapabilities 983 { 984 Optional!WorkspaceClientCapabilities workspace; 985 Optional!TextDocumentClientCapabilities textDocument; 986 JSONValue experimental; 987 } 988 989 unittest 990 { 991 string json = q{{ 992 "workspace": { 993 "configuration": true 994 } 995 }}; 996 auto caps = json.parseJSON.fromJSON!ClientCapabilities; 997 assert(caps.workspace.configuration); 998 } 999 1000 struct InitializeResult 1001 { 1002 ServerCapabilities capabilities; 1003 } 1004 1005 struct InitializeError 1006 { 1007 bool retry; 1008 } 1009 1010 enum TextDocumentSyncKind 1011 { 1012 none, 1013 full, 1014 incremental 1015 } 1016 1017 struct CompletionOptions 1018 { 1019 bool resolveProvider; 1020 string[] triggerCharacters; 1021 } 1022 1023 struct SignatureHelpOptions 1024 { 1025 string[] triggerCharacters; 1026 } 1027 1028 struct CodeLensOptions 1029 { 1030 bool resolveProvider; 1031 } 1032 1033 struct DocumentOnTypeFormattingOptions 1034 { 1035 mixin StrictOptionalSerializer; 1036 1037 string firstTriggerCharacter; 1038 Optional!(string[]) moreTriggerCharacter; 1039 } 1040 1041 struct DocumentLinkOptions 1042 { 1043 bool resolveProvider; 1044 } 1045 1046 struct ColorProviderOptions 1047 { 1048 } 1049 1050 struct ExecuteCommandOptions 1051 { 1052 string[] commands; 1053 } 1054 1055 struct SaveOptions 1056 { 1057 bool includeText; 1058 } 1059 1060 struct TextDocumentSyncOptions 1061 { 1062 bool openClose; 1063 int change; 1064 bool willSave; 1065 bool willSaveWaitUntil; 1066 SaveOptions save; 1067 } 1068 1069 struct ServerCapabilities 1070 { 1071 mixin StrictOptionalSerializer; 1072 1073 JSONValue textDocumentSync; 1074 bool hoverProvider; 1075 Optional!CompletionOptions completionProvider; 1076 Optional!SignatureHelpOptions signatureHelpProvider; 1077 bool definitionProvider; 1078 Optional!bool typeDefinitionProvider; 1079 Optional!bool implementationProvider; 1080 bool referencesProvider; 1081 bool documentHighlightProvider; 1082 bool documentSymbolProvider; 1083 bool workspaceSymbolProvider; 1084 bool codeActionProvider; 1085 Optional!CodeLensOptions codeLensProvider; 1086 bool documentFormattingProvider; 1087 bool documentRangeFormattingProvider; 1088 Optional!DocumentOnTypeFormattingOptions documentOnTypeFormattingProvider; 1089 bool renameProvider; 1090 Optional!DocumentLinkOptions documentLinkProvider; 1091 Optional!ColorProviderOptions colorProvider; 1092 Optional!ExecuteCommandOptions executeCommandProvider; 1093 Optional!ServerWorkspaceCapabilities workspace; 1094 JSONValue experimental; 1095 } 1096 1097 struct ServerWorkspaceCapabilities 1098 { 1099 mixin StrictOptionalSerializer; 1100 1101 struct WorkspaceFolders 1102 { 1103 mixin StrictOptionalSerializer; 1104 1105 Optional!bool supported; 1106 Optional!bool changeNotifications; 1107 } 1108 1109 Optional!WorkspaceFolders workspaceFolders; 1110 } 1111 1112 struct ShowMessageParams 1113 { 1114 MessageType type; 1115 string message; 1116 } 1117 1118 struct ShowMessageRequestParams 1119 { 1120 mixin StrictOptionalSerializer; 1121 1122 MessageType type; 1123 string message; 1124 Optional!(MessageActionItem[]) actions; 1125 } 1126 1127 struct MessageActionItem 1128 { 1129 string title; 1130 } 1131 1132 struct LogMessageParams 1133 { 1134 MessageType type; 1135 string message; 1136 } 1137 1138 struct Registration 1139 { 1140 string id; 1141 string method; 1142 JSONValue registerOptions; 1143 } 1144 1145 struct RegistrationParams 1146 { 1147 Registration[] registrations; 1148 } 1149 1150 struct TextDocumentRegistrationOptions 1151 { 1152 mixin StrictOptionalSerializer; 1153 1154 Optional!DocumentSelector documentSelector; 1155 } 1156 1157 struct Unregistration 1158 { 1159 string id; 1160 string method; 1161 } 1162 1163 struct UnregistrationParams 1164 { 1165 Unregistration[] unregistrations; 1166 } 1167 1168 struct DidChangeConfigurationParams 1169 { 1170 JSONValue settings; 1171 } 1172 1173 struct ConfigurationParams 1174 { 1175 ConfigurationItem[] items; 1176 } 1177 1178 struct ConfigurationItem 1179 { 1180 mixin StrictOptionalSerializer; 1181 1182 Optional!string scopeUri; 1183 Optional!string section; 1184 } 1185 1186 struct DidOpenTextDocumentParams 1187 { 1188 TextDocumentItem textDocument; 1189 } 1190 1191 struct DidChangeTextDocumentParams 1192 { 1193 VersionedTextDocumentIdentifier textDocument; 1194 TextDocumentContentChangeEvent[] contentChanges; 1195 } 1196 1197 struct TextDocumentContentChangeEvent 1198 { 1199 mixin StrictOptionalSerializer; 1200 1201 Optional!TextRange range; 1202 Optional!int rangeLength; 1203 string text; 1204 } 1205 1206 struct TextDocumentChangeRegistrationOptions 1207 { 1208 mixin StrictOptionalSerializer; 1209 1210 Optional!DocumentSelector documentSelector; 1211 TextDocumentSyncKind syncKind; 1212 } 1213 1214 struct WillSaveTextDocumentParams 1215 { 1216 TextDocumentIdentifier textDocument; 1217 TextDocumentSaveReason reason; 1218 } 1219 1220 enum TextDocumentSaveReason 1221 { 1222 manual = 1, 1223 afterDelay, 1224 focusOut 1225 } 1226 1227 struct DidSaveTextDocumentParams 1228 { 1229 mixin StrictOptionalSerializer; 1230 1231 TextDocumentIdentifier textDocument; 1232 Optional!string text; 1233 } 1234 1235 struct TextDocumentSaveRegistrationOptions 1236 { 1237 mixin StrictOptionalSerializer; 1238 1239 Optional!DocumentSelector documentSelector; 1240 bool includeText; 1241 } 1242 1243 struct DidCloseTextDocumentParams 1244 { 1245 TextDocumentIdentifier textDocument; 1246 } 1247 1248 struct DidChangeWatchedFilesParams 1249 { 1250 FileEvent[] changes; 1251 } 1252 1253 struct FileEvent 1254 { 1255 DocumentUri uri; 1256 FileChangeType type; 1257 } 1258 1259 enum FileChangeType 1260 { 1261 created = 1, 1262 changed, 1263 deleted 1264 } 1265 1266 struct PublishDiagnosticsParams 1267 { 1268 DocumentUri uri; 1269 Diagnostic[] diagnostics; 1270 } 1271 1272 struct CompletionList 1273 { 1274 bool isIncomplete; 1275 CompletionItem[] items; 1276 } 1277 1278 enum InsertTextFormat 1279 { 1280 plainText = 1, 1281 snippet 1282 } 1283 1284 struct CompletionItem 1285 { 1286 mixin StrictOptionalSerializer; 1287 1288 string label; 1289 Optional!CompletionItemKind kind; 1290 Optional!string detail; 1291 Optional!MarkupContent documentation; 1292 @SerializedName("deprecated") Optional!bool deprecated_; 1293 Optional!bool preselect; 1294 Optional!string sortText; 1295 Optional!string filterText; 1296 Optional!string insertText; 1297 Optional!InsertTextFormat insertTextFormat; 1298 Optional!TextEdit textEdit; 1299 Optional!(TextEdit[]) additionalTextEdits; 1300 Optional!(string[]) commitCharacters; 1301 Optional!Command command; 1302 JSONValue data; 1303 } 1304 1305 enum CompletionItemKind 1306 { 1307 text = 1, 1308 method, 1309 function_, 1310 constructor, 1311 field, 1312 variable, 1313 class_, 1314 interface_, 1315 module_, 1316 property, 1317 unit, 1318 value, 1319 enum_, 1320 keyword, 1321 snippet, 1322 color, 1323 file, 1324 reference, 1325 folder, 1326 enumMember, 1327 constant, 1328 struct_, 1329 event, 1330 operator, 1331 typeParameter 1332 } 1333 1334 struct CompletionRegistrationOptions 1335 { 1336 Optional!DocumentSelector documentSelector; 1337 Optional!(string[]) triggerCharacters; 1338 bool resolveProvider; 1339 } 1340 1341 struct Hover 1342 { 1343 ArrayOrSingle!MarkedString contents; 1344 Optional!TextRange range; 1345 } 1346 1347 struct MarkedString 1348 { 1349 string value; 1350 string language; 1351 1352 const JSONValue _toJSON() 1353 { 1354 if (!language.length) 1355 return JSONValue(value); 1356 else 1357 return JSONValue([ 1358 "value": JSONValue(value), 1359 "language": JSONValue(language) 1360 ]); 1361 } 1362 1363 static MarkedString fromJSON(JSONValue val) 1364 { 1365 MarkedString ret; 1366 if (val.type == JSONType..string) 1367 ret.value = val.str; 1368 else 1369 { 1370 ret.value = val["value"].str; 1371 ret.language = val["language"].str; 1372 } 1373 return ret; 1374 } 1375 } 1376 1377 enum MarkupKind : string 1378 { 1379 plaintext = "plaintext", 1380 markdown = "markdown" 1381 } 1382 1383 struct MarkupContent 1384 { 1385 string kind; 1386 string value; 1387 1388 this(MarkupKind kind, string value) 1389 { 1390 this.kind = kind; 1391 this.value = value; 1392 } 1393 1394 this(string text) 1395 { 1396 kind = MarkupKind.plaintext; 1397 value = text; 1398 } 1399 1400 this(MarkedString[] markup) 1401 { 1402 kind = MarkupKind.markdown; 1403 foreach (block; markup) 1404 { 1405 if (block.language.length) 1406 { 1407 value ~= "```" ~ block.language ~ "\n"; 1408 value ~= block.value; 1409 value ~= "```"; 1410 } 1411 else 1412 value ~= block.value; 1413 value ~= "\n\n"; 1414 } 1415 } 1416 } 1417 1418 struct SignatureHelp 1419 { 1420 SignatureInformation[] signatures; 1421 Optional!int activeSignature; 1422 Optional!int activeParameter; 1423 1424 this(SignatureInformation[] signatures) 1425 { 1426 this.signatures = signatures; 1427 } 1428 1429 this(SignatureInformation[] signatures, int activeSignature, int activeParameter) 1430 { 1431 this.signatures = signatures; 1432 this.activeSignature = activeSignature; 1433 this.activeParameter = activeParameter; 1434 } 1435 } 1436 1437 struct SignatureInformation 1438 { 1439 string label; 1440 Optional!MarkupContent documentation; 1441 Optional!(ParameterInformation[]) parameters; 1442 } 1443 1444 struct ParameterInformation 1445 { 1446 JSONValue label; 1447 Optional!MarkupContent documentation; 1448 } 1449 1450 struct SignatureHelpRegistrationOptions 1451 { 1452 Optional!DocumentSelector documentSelector; 1453 Optional!(string[]) triggerCharacters; 1454 } 1455 1456 struct ReferenceParams 1457 { 1458 TextDocumentIdentifier textDocument; 1459 Position position; 1460 ReferenceContext context; 1461 } 1462 1463 struct ReferenceContext 1464 { 1465 bool includeDeclaration; 1466 } 1467 1468 struct DocumentHighlightParams 1469 { 1470 TextDocumentIdentifier textDocument; 1471 Position position; 1472 } 1473 1474 struct DocumentHighlight 1475 { 1476 TextRange range; 1477 Optional!DocumentHighlightKind kind; 1478 } 1479 1480 enum DocumentHighlightKind 1481 { 1482 text = 1, 1483 read, 1484 write 1485 } 1486 1487 struct DocumentSymbolParams 1488 { 1489 TextDocumentIdentifier textDocument; 1490 } 1491 1492 struct SymbolInformation 1493 { 1494 string name; 1495 SymbolKind kind; 1496 Location location; 1497 Optional!string containerName; 1498 } 1499 1500 struct DocumentSymbol 1501 { 1502 mixin StrictOptionalSerializer; 1503 1504 string name; 1505 Optional!string detail; 1506 SymbolKind kind; 1507 @SerializedName("deprecated") Optional!bool deprecated_; 1508 TextRange range; 1509 TextRange selectionRange; 1510 DocumentSymbol[] children; 1511 @SerializeIgnore string parent; 1512 } 1513 1514 enum SymbolKind 1515 { 1516 file = 1, 1517 module_, 1518 namespace, 1519 package_, 1520 class_, 1521 method, 1522 property, 1523 field, 1524 constructor, 1525 enum_, 1526 interface_, 1527 function_, 1528 variable, 1529 constant, 1530 string, 1531 number, 1532 boolean, 1533 array, 1534 object, 1535 key, 1536 null_, 1537 enumMember, 1538 struct_, 1539 event, 1540 operator, 1541 typeParameter 1542 } 1543 1544 struct WorkspaceSymbolParams 1545 { 1546 string query; 1547 } 1548 1549 struct CodeActionParams 1550 { 1551 TextDocumentIdentifier textDocument; 1552 TextRange range; 1553 CodeActionContext context; 1554 } 1555 1556 struct CodeActionContext 1557 { 1558 Diagnostic[] diagnostics; 1559 } 1560 1561 struct CodeLensParams 1562 { 1563 TextDocumentIdentifier textDocument; 1564 } 1565 1566 struct CodeLens 1567 { 1568 TextRange range; 1569 Optional!Command command; 1570 JSONValue data; 1571 } 1572 1573 struct CodeLensRegistrationOptions 1574 { 1575 Optional!DocumentSelector documentSelector; 1576 bool resolveProvider; 1577 } 1578 1579 struct DocumentLinkParams 1580 { 1581 TextDocumentIdentifier textDocument; 1582 } 1583 1584 struct DocumentLink 1585 { 1586 TextRange range; 1587 DocumentUri target; 1588 } 1589 1590 struct DocumentLinkRegistrationOptions 1591 { 1592 Optional!DocumentSelector documentSelector; 1593 bool resolveProvider; 1594 } 1595 1596 struct DocumentColorParams 1597 { 1598 TextDocumentIdentifier textDocument; 1599 } 1600 1601 struct ColorInformation 1602 { 1603 TextRange range; 1604 Color color; 1605 } 1606 1607 struct Color 1608 { 1609 double red = 0; 1610 double green = 0; 1611 double blue = 0; 1612 double alpha = 1; 1613 } 1614 1615 struct ColorPresentationParams 1616 { 1617 TextDocumentIdentifier textDocument; 1618 Color color; 1619 TextRange range; 1620 } 1621 1622 struct ColorPresentation 1623 { 1624 string label; 1625 Optional!TextEdit textEdit; 1626 Optional!(TextEdit[]) additionalTextEdits; 1627 } 1628 1629 struct DocumentFormattingParams 1630 { 1631 TextDocumentIdentifier textDocument; 1632 FormattingOptions options; 1633 } 1634 1635 struct FormattingOptions 1636 { 1637 int tabSize; 1638 bool insertSpaces; 1639 JSONValue data; 1640 } 1641 1642 struct DocumentRangeFormattingParams 1643 { 1644 TextDocumentIdentifier textDocument; 1645 TextRange range; 1646 FormattingOptions options; 1647 } 1648 1649 struct DocumentOnTypeFormattingParams 1650 { 1651 TextDocumentIdentifier textDocument; 1652 Position position; 1653 string ch; 1654 FormattingOptions options; 1655 } 1656 1657 struct DocumentOnTypeFormattingRegistrationOptions 1658 { 1659 Optional!DocumentSelector documentSelector; 1660 string firstTriggerCharacter; 1661 Optional!(string[]) moreTriggerCharacter; 1662 } 1663 1664 struct RenameParams 1665 { 1666 TextDocumentIdentifier textDocument; 1667 Position position; 1668 string newName; 1669 } 1670 1671 struct ExecuteCommandParams 1672 { 1673 string command; 1674 Optional!(JSONValue[]) arguments; 1675 } 1676 1677 struct ExecuteCommandRegistrationOptions 1678 { 1679 string[] commands; 1680 } 1681 1682 struct ApplyWorkspaceEditParams 1683 { 1684 WorkspaceEdit edit; 1685 } 1686 1687 struct ApplyWorkspaceEditResponse 1688 { 1689 bool applied; 1690 } 1691 1692 struct WorkspaceFolder 1693 { 1694 string uri; 1695 string name; 1696 } 1697 1698 struct DidChangeWorkspaceFoldersParams 1699 { 1700 WorkspaceFoldersChangeEvent event; 1701 } 1702 1703 struct WorkspaceFoldersChangeEvent 1704 { 1705 WorkspaceFolder[] added; 1706 WorkspaceFolder[] removed; 1707 }