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

Generated by: LCOV version 1.10