LCOV - code coverage report
Current view: top level - src/torque/ls - message-handler.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 49 98 50.0 %
Date: 2019-04-17 Functions: 5 12 41.7 %

          Line data    Source code
       1             : // Copyright 2019 the V8 project authors. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style license that can be
       3             : // found in the LICENSE file.
       4             : 
       5             : #include <algorithm>
       6             : #include "src/torque/ls/message-handler.h"
       7             : 
       8             : #include "src/torque/ls/globals.h"
       9             : #include "src/torque/ls/json-parser.h"
      10             : #include "src/torque/ls/message-pipe.h"
      11             : #include "src/torque/ls/message.h"
      12             : #include "src/torque/server-data.h"
      13             : #include "src/torque/source-positions.h"
      14             : #include "src/torque/torque-compiler.h"
      15             : 
      16             : namespace v8 {
      17             : namespace internal {
      18             : namespace torque {
      19             : 
      20           0 : DEFINE_CONTEXTUAL_VARIABLE(Logger)
      21           0 : DEFINE_CONTEXTUAL_VARIABLE(TorqueFileList)
      22             : 
      23             : namespace ls {
      24             : 
      25             : static const char kContentLength[] = "Content-Length: ";
      26             : static const size_t kContentLengthSize = sizeof(kContentLength) - 1;
      27             : 
      28             : #ifdef V8_OS_WIN
      29             : // On Windows, in text mode, \n is translated to \r\n.
      30             : constexpr const char* kProtocolLineEnding = "\n\n";
      31             : #else
      32             : constexpr const char* kProtocolLineEnding = "\r\n\r\n";
      33             : #endif
      34             : 
      35           0 : JsonValue ReadMessage() {
      36             :   std::string line;
      37           0 :   std::getline(std::cin, line);
      38             : 
      39           0 :   if (line.rfind(kContentLength) != 0) {
      40             :     // Invalid message, we just crash.
      41           0 :     Logger::Log("[fatal] Did not find Content-Length ...\n");
      42           0 :     v8::base::OS::Abort();
      43             :   }
      44             : 
      45           0 :   const int content_length = std::atoi(line.substr(kContentLengthSize).c_str());
      46           0 :   std::getline(std::cin, line);
      47           0 :   std::string content(content_length, ' ');
      48           0 :   std::cin.read(&content[0], content_length);
      49             : 
      50           0 :   Logger::Log("[incoming] ", content, "\n\n");
      51             : 
      52           0 :   return ParseJson(content).value;
      53             : }
      54             : 
      55           0 : void WriteMessage(JsonValue& message) {
      56           0 :   std::string content = SerializeToString(message);
      57             : 
      58           0 :   Logger::Log("[outgoing] ", content, "\n\n");
      59             : 
      60           0 :   std::cout << kContentLength << content.size() << kProtocolLineEnding;
      61             :   std::cout << content << std::flush;
      62           0 : }
      63             : 
      64             : namespace {
      65             : 
      66           0 : void RecompileTorque() {
      67           0 :   Logger::Log("[info] Start compilation run ...\n");
      68             : 
      69             :   TorqueCompilerOptions options;
      70             :   options.output_directory = "";
      71           0 :   options.verbose = false;
      72           0 :   options.collect_language_server_data = true;
      73           0 :   options.abort_on_lint_errors = false;
      74             : 
      75           0 :   TorqueCompilerResult result = CompileTorque(TorqueFileList::Get(), options);
      76             : 
      77             :   LanguageServerData::Get() = result.language_server_data;
      78             :   SourceFileMap::Get() = result.source_file_map;
      79             : 
      80           0 :   Logger::Log("[info] Finished compilation run ...\n");
      81           0 : }
      82             : 
      83           1 : void HandleInitializeRequest(InitializeRequest request, MessageWriter writer) {
      84             :   InitializeResponse response;
      85           1 :   response.set_id(request.id());
      86           1 :   response.result().capabilities().textDocumentSync();
      87           1 :   response.result().capabilities().set_definitionProvider(true);
      88             : 
      89             :   // TODO(szuend): Register for document synchronisation here,
      90             :   //               so we work with the content that the client
      91             :   //               provides, not directly read from files.
      92             :   // TODO(szuend): Check that the client actually supports dynamic
      93             :   //               "workspace/didChangeWatchedFiles" capability.
      94             :   // TODO(szuend): Check if client supports "LocationLink". This will
      95             :   //               influence the result of "goto definition".
      96           1 :   writer(response.GetJsonValue());
      97           1 : }
      98             : 
      99           1 : void HandleInitializedNotification(MessageWriter writer) {
     100             :   RegistrationRequest request;
     101             :   // TODO(szuend): The language server needs a "global" request id counter.
     102           1 :   request.set_id(2000);
     103           2 :   request.set_method("client/registerCapability");
     104             : 
     105           1 :   Registration reg = request.params().add_registrations();
     106             :   auto options =
     107           1 :       reg.registerOptions<DidChangeWatchedFilesRegistrationOptions>();
     108           1 :   FileSystemWatcher watcher = options.add_watchers();
     109           2 :   watcher.set_globPattern("**/*.tq");
     110           1 :   watcher.set_kind(FileSystemWatcher::WatchKind::kAll);
     111             : 
     112           2 :   reg.set_id("did-change-id");
     113           2 :   reg.set_method("workspace/didChangeWatchedFiles");
     114             : 
     115           1 :   writer(request.GetJsonValue());
     116           1 : }
     117             : 
     118           0 : void HandleTorqueFileListNotification(TorqueFileListNotification notification) {
     119           0 :   CHECK_EQ(notification.params().object()["files"].tag, JsonValue::ARRAY);
     120             : 
     121             :   std::vector<std::string>& files = TorqueFileList::Get();
     122           0 :   Logger::Log("[info] Initial file list:\n");
     123           0 :   for (const auto& file_json :
     124           0 :        notification.params().object()["files"].ToArray()) {
     125           0 :     CHECK(file_json.IsString());
     126             : 
     127             :     // We only consider file URIs (there shouldn't be anything else).
     128             :     // Internally we store the URI instead of the path, eliminating the need
     129             :     // to encode it again.
     130           0 :     files.push_back(file_json.ToString());
     131           0 :     Logger::Log("    ", file_json.ToString(), "\n");
     132             :   }
     133             : 
     134             :   // The Torque compiler expects to see some files first,
     135             :   // we need to order them in the correct way.
     136             :   // TODO(szuend): Remove this, once the compiler doesn't require the input
     137             :   //               files to be in a specific order.
     138             :   std::vector<std::string> sort_to_front = {"base.tq", "frames.tq",
     139           0 :                                             "arguments.tq", "array.tq"};
     140           0 :   std::sort(files.begin(), files.end(), [&](std::string a, std::string b) {
     141           0 :     for (const std::string& fixed_file : sort_to_front) {
     142           0 :       if (a.find(fixed_file) != std::string::npos) return true;
     143           0 :       if (b.find(fixed_file) != std::string::npos) return false;
     144             :     }
     145           0 :     return a < b;
     146             :   });
     147             : 
     148           0 :   RecompileTorque();
     149           0 : }
     150             : 
     151           3 : void HandleGotoDefinitionRequest(GotoDefinitionRequest request,
     152             :                                  MessageWriter writer) {
     153             :   GotoDefinitionResponse response;
     154           3 :   response.set_id(request.id());
     155             : 
     156             :   SourceId id =
     157           3 :       SourceFileMap::GetSourceId(request.params().textDocument().uri());
     158             : 
     159             :   // Unknown source files cause an empty response which corresponds with
     160             :   // the definition not beeing found.
     161           3 :   if (!id.IsValid()) {
     162           2 :     response.SetNull("result");
     163           1 :     writer(response.GetJsonValue());
     164             :     return;
     165             :   }
     166             : 
     167           4 :   LineAndColumn pos{request.params().position().line(),
     168           4 :                     request.params().position().character()};
     169             : 
     170           2 :   if (auto maybe_definition = LanguageServerData::FindDefinition(id, pos)) {
     171           1 :     SourcePosition definition = *maybe_definition;
     172             : 
     173           1 :     std::string definition_file = SourceFileMap::GetSource(definition.source);
     174           1 :     response.result().set_uri(definition_file);
     175             : 
     176           1 :     Range range = response.result().range();
     177           1 :     range.start().set_line(definition.start.line);
     178           1 :     range.start().set_character(definition.start.column);
     179           1 :     range.end().set_line(definition.end.line);
     180           1 :     range.end().set_character(definition.end.column);
     181             :   } else {
     182           2 :     response.SetNull("result");
     183             :   }
     184             : 
     185           2 :   writer(response.GetJsonValue());
     186             : }
     187             : 
     188             : void HandleChangeWatchedFilesNotification(
     189             :     DidChangeWatchedFilesNotification notification) {
     190             :   // TODO(szuend): Implement updates to the TorqueFile list when create/delete
     191             :   //               notifications are received. Currently we simply re-compile.
     192           0 :   RecompileTorque();
     193             : }
     194             : 
     195             : }  // namespace
     196             : 
     197           5 : void HandleMessage(JsonValue& raw_message, MessageWriter writer) {
     198             :   Request<bool> request(raw_message);
     199             : 
     200             :   // We ignore responses for now. They are matched to requests
     201             :   // by id and don't have a method set.
     202             :   // TODO(szuend): Implement proper response handling for requests
     203             :   //               that originate from the server.
     204           5 :   if (!request.has_method()) {
     205           0 :     Logger::Log("[info] Unhandled response with id ", request.id(), "\n\n");
     206             :     return;
     207             :   }
     208             : 
     209           5 :   const std::string method = request.method();
     210           5 :   if (method == "initialize") {
     211           2 :     HandleInitializeRequest(InitializeRequest(request.GetJsonValue()), writer);
     212           4 :   } else if (method == "initialized") {
     213           1 :     HandleInitializedNotification(writer);
     214           3 :   } else if (method == "torque/fileList") {
     215           0 :     HandleTorqueFileListNotification(
     216           0 :         TorqueFileListNotification(request.GetJsonValue()));
     217           3 :   } else if (method == "textDocument/definition") {
     218           3 :     HandleGotoDefinitionRequest(GotoDefinitionRequest(request.GetJsonValue()),
     219           3 :                                 writer);
     220           0 :   } else if (method == "workspace/didChangeWatchedFiles") {
     221           0 :     HandleChangeWatchedFilesNotification(
     222             :         DidChangeWatchedFilesNotification(request.GetJsonValue()));
     223             :   } else {
     224           0 :     Logger::Log("[error] Message of type ", method, " is not handled!\n\n");
     225             :   }
     226             : }
     227             : 
     228             : }  // namespace ls
     229             : }  // namespace torque
     230             : }  // namespace internal
     231       59454 : }  // namespace v8

Generated by: LCOV version 1.10