DisplayTestContext.hx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import haxe.io.Bytes;
  2. import haxe.io.BytesBuffer;
  3. using StringTools;
  4. import Types;
  5. class HaxeInvocationException {
  6. public var message:String;
  7. public var fieldName:String;
  8. public var arguments:Array<String>;
  9. public var source:String;
  10. public function new(message:String, fieldName:String, arguments:Array<String>, source:String) {
  11. this.message = message;
  12. this.fieldName = fieldName;
  13. this.arguments = arguments;
  14. this.source = source;
  15. }
  16. public function toString() {
  17. return 'HaxeInvocationException($message, $fieldName, $arguments, $source])';
  18. }
  19. }
  20. class DisplayTestContext {
  21. static var haxeServer = haxeserver.HaxeServerSync.launch("haxe", []);
  22. var markers:Map<Int, Int>;
  23. var fieldName:String;
  24. public final source:File;
  25. public function new(path:String, fieldName:String, source:String, markers:Map<Int, Int>) {
  26. this.fieldName = fieldName;
  27. this.source = new File(path, source);
  28. this.markers = markers;
  29. }
  30. public function pos(id:Int):Position {
  31. var r = markers[id];
  32. if (r == null)
  33. throw "No such marker: " + id;
  34. return new Position(r);
  35. }
  36. public function range(pos1:Int, pos2:Int) {
  37. return normalizePath(source.formatRange(pos(pos1), pos(pos2)));
  38. }
  39. public function fields(pos:Position):Array<FieldElement> {
  40. return extractFields(callHaxe('$pos'));
  41. }
  42. public function signatures(pos:Position):Array<String> {
  43. return extractSignatures(callHaxe('$pos'));
  44. }
  45. public function toplevel(pos:Position):Array<ToplevelElement> {
  46. return extractToplevel(callHaxe('$pos@toplevel'));
  47. }
  48. public function type(pos:Position):String {
  49. return extractType(callHaxe('$pos@type'));
  50. }
  51. public function positions(pos:Position):Array<String> {
  52. return extractPositions(callHaxe('$pos@position'));
  53. }
  54. public function position(pos:Position):String {
  55. return positions(pos)[0];
  56. }
  57. public function usage(pos:Position):Array<String> {
  58. return extractPositions(callHaxe('$pos@usage'));
  59. }
  60. public function documentSymbols():Array<ModuleSymbolEntry> {
  61. return haxe.Json.parse(callHaxe("0@module-symbols"))[0].symbols;
  62. }
  63. public function signature(pos:Position):SignatureHelp {
  64. return haxe.Json.parse(callHaxe('$pos@signature'));
  65. }
  66. public function metadataDoc(pos:Position):String {
  67. return extractMetadata(callHaxe('$pos@type'));
  68. }
  69. public function diagnostics():Array<Diagnostic<Dynamic>> {
  70. var result = haxe.Json.parse(callHaxe('0@diagnostics'))[0];
  71. return if (result == null) [] else result.diagnostics;
  72. }
  73. public function hasErrorMessage(f:Void->Void, message:String) {
  74. return try {
  75. f();
  76. false;
  77. } catch (exc:HaxeInvocationException) {
  78. return exc.message.indexOf(message) != -1;
  79. }
  80. }
  81. function callHaxe(displayPart:String) {
  82. var args = ["--display", source.path + "@" + displayPart];
  83. var result = runHaxe(args, source.content);
  84. if (result.hasError || result.stderr == "") {
  85. throw new HaxeInvocationException(result.stderr, fieldName, args, source.content);
  86. }
  87. return result.stderr;
  88. }
  89. static public function runHaxe(args:Array<String>, ?stdin:String) {
  90. return haxeServer.rawRequest(args, stdin == null ? null : Bytes.ofString(stdin));
  91. }
  92. static function extractType(result:String) {
  93. var xml = Xml.parse(result);
  94. xml = xml.firstElement();
  95. if (xml.nodeName != "type") {
  96. return null;
  97. }
  98. return StringTools.trim(xml.firstChild().nodeValue);
  99. }
  100. static function extractSignatures(result:String) {
  101. var xml = Xml.parse('<x>$result</x>');
  102. xml = xml.firstElement();
  103. var ret = [];
  104. for (xml in xml.elementsNamed("type")) {
  105. ret.push(StringTools.trim(xml.firstChild().nodeValue));
  106. }
  107. return ret;
  108. }
  109. static function extractPositions(result:String) {
  110. var xml = Xml.parse(result);
  111. xml = xml.firstElement();
  112. if (xml.nodeName != "list") {
  113. return null;
  114. }
  115. var ret = [];
  116. for (xml in xml.elementsNamed("pos")) {
  117. ret.push(normalizePath(xml.firstChild().nodeValue.trim()));
  118. }
  119. return ret;
  120. }
  121. static function extractToplevel(result:String) {
  122. var xml = Xml.parse(result);
  123. xml = xml.firstElement();
  124. if (xml.nodeName != "il") {
  125. return null;
  126. }
  127. var ret = [];
  128. for (xml in xml.elementsNamed("i")) {
  129. ret.push({kind: xml.get("k"), type: xml.get("t"), name: xml.firstChild().nodeValue});
  130. }
  131. return ret;
  132. }
  133. static function extractFields(result:String) {
  134. var xml = Xml.parse(result);
  135. xml = xml.firstElement();
  136. if (xml.nodeName != "list") {
  137. return null;
  138. }
  139. var ret = [];
  140. for (xml in xml.elementsNamed("i")) {
  141. ret.push({name: xml.get("n"), type: xml.firstElement().firstChild().nodeValue, kind: xml.get("k")});
  142. }
  143. return ret;
  144. }
  145. static function extractMetadata(result:String) {
  146. var xml = Xml.parse(result);
  147. xml = xml.firstElement();
  148. if (xml.nodeName != "metadata") {
  149. return null;
  150. }
  151. return xml.firstChild().nodeValue;
  152. }
  153. static function normalizePath(p:String):String {
  154. if (!haxe.io.Path.isAbsolute(p)) {
  155. p = Sys.getCwd() + p;
  156. }
  157. if (Sys.systemName() == "Windows") {
  158. // on windows, haxe returns paths with backslashes, drive letter uppercased
  159. p = p.substr(0, 1).toUpperCase() + p.substr(1);
  160. p = p.replace("/", "\\");
  161. }
  162. return p;
  163. }
  164. }