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 }