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