@@ -517,10 +517,62 @@ struct SplitState {
517517 int file_index = 0 ;
518518};
519519
520+ // Reformats `[[@LSP:` and `[[LSP-NOTIFY:` as an LSP call with headers. For
521+ // notifications, `lsp_call_id` is null.
522+ static auto ReplaceLspKeywordAt (std::string* content, size_t keyword_pos,
523+ int * lsp_call_id, llvm::StringLiteral keyword,
524+ llvm::StringLiteral method_or_id_label)
525+ -> ErrorOr<size_t> {
526+ auto method_or_id_start = keyword_pos + keyword.size ();
527+
528+ static constexpr llvm::StringLiteral LspEnd = " ]]" ;
529+ auto keyword_end = content->find (" ]]" , method_or_id_start);
530+ if (keyword_end == std::string::npos) {
531+ return ErrorBuilder () << " Missing `" << LspEnd << " ` after `" << keyword
532+ << " `" ;
533+ }
534+
535+ auto method_or_id_end = content->find (" :" , method_or_id_start);
536+ auto extra_content_start = method_or_id_end + 1 ;
537+ if (method_or_id_end == std::string::npos || method_or_id_end > keyword_end) {
538+ method_or_id_end = keyword_end;
539+ extra_content_start = keyword_end;
540+ }
541+ auto method_or_id =
542+ llvm::StringRef (*content).slice (method_or_id_start, method_or_id_end);
543+
544+ auto extra_content =
545+ llvm::StringRef (*content).slice (extra_content_start, keyword_end);
546+ std::string extra_content_sep;
547+ if (!extra_content.empty ()) {
548+ extra_content_sep = " ," ;
549+ if (!extra_content.starts_with (" \n " )) {
550+ extra_content_sep += " " ;
551+ }
552+ }
553+
554+ // Form the JSON.
555+ std::string json = R"( {"jsonrpc": "2.0", )" ;
556+ if (lsp_call_id) {
557+ json += llvm::formatv (R"( "id": "{0}", )" , ++(*lsp_call_id));
558+ }
559+ json += llvm::formatv (R"( "{0}": "{1}"{2}{3}})" , method_or_id_label,
560+ method_or_id, extra_content_sep, extra_content);
561+
562+ // Add the Content-Length header. The `2` accounts for extra newlines.
563+ auto json_with_header =
564+ llvm::formatv (" Content-Length: {0}\n\n {1}\n " , json.size () + 2 , json)
565+ .str ();
566+ // Insert the content.
567+ content->replace (keyword_pos, keyword_end + 2 - keyword_pos,
568+ json_with_header);
569+ return keyword_pos + json_with_header.size ();
570+ }
571+
520572// Replaces the keyword at the given position. Returns the position to start a
521573// find for the next keyword.
522574static auto ReplaceContentKeywordAt (std::string* content, size_t keyword_pos,
523- llvm::StringRef test_name, int * lsp_id )
575+ llvm::StringRef test_name, int * lsp_call_id )
524576 -> ErrorOr<size_t> {
525577 auto keyword = llvm::StringRef (*content).substr (keyword_pos);
526578
@@ -538,57 +590,20 @@ static auto ReplaceContentKeywordAt(std::string* content, size_t keyword_pos,
538590 return keyword_pos + test_name.size ();
539591 }
540592
541- // Reformatted as an LSP call with headers.
542593 static constexpr llvm::StringLiteral Lsp = " [[@LSP:" ;
543594 if (keyword.starts_with (Lsp)) {
544- auto method_start = keyword_pos + Lsp.size ();
545-
546- static constexpr llvm::StringLiteral LspEnd = " ]]" ;
547- auto keyword_end = content->find (" ]]" , method_start);
548- if (keyword_end == std::string::npos) {
549- return ErrorBuilder ()
550- << " Missing `" << LspEnd << " ` after `" << Lsp << " `" ;
551- }
552-
553- auto method_end = content->find (" :" , method_start);
554- auto extra_content_start = method_end + 1 ;
555- if (method_end == std::string::npos || method_end > keyword_end) {
556- method_end = keyword_end;
557- extra_content_start = keyword_end;
558- }
559- auto method = content->substr (method_start, method_end - method_start);
560-
561- auto extra_content =
562- content->substr (extra_content_start, keyword_end - extra_content_start);
563- std::string extra_content_sep;
564- if (!extra_content.empty ()) {
565- extra_content_sep = " ," ;
566- if (!extra_content.starts_with (" \n " )) {
567- extra_content_sep += " " ;
568- }
569- }
570-
571- // Form the JSON.
572- std::string json;
573- if (method == " exit" ) {
574- if (!extra_content.empty ()) {
575- return Error (" `[[@LSP:exit:` cannot include extra content" );
576- }
577- json = R"( {"jsonrpc": "2.0", "method": "exit"})" ;
578- } else {
579- json = llvm::formatv (
580- R"( {{"jsonrpc": "2.0", "id": "{0}", "method": "{1}"{2}{3}})" ,
581- ++(*lsp_id), method, extra_content_sep, extra_content)
582- .str ();
583- }
584- // Add the Content-Length header. The `2` accounts for extra newlines.
585- auto json_with_header =
586- llvm::formatv (" Content-Length: {0}\n\n {1}\n " , json.size () + 2 , json)
587- .str ();
588- // Insert the content.
589- content->replace (keyword_pos, keyword_end + 2 - keyword_pos,
590- json_with_header);
591- return keyword_pos + json_with_header.size ();
595+ return ReplaceLspKeywordAt (content, keyword_pos, lsp_call_id, Lsp,
596+ " method" );
597+ }
598+ static constexpr llvm::StringLiteral LspNotify = " [[@LSP-NOTIFY:" ;
599+ if (keyword.starts_with (LspNotify)) {
600+ return ReplaceLspKeywordAt (content, keyword_pos,
601+ /* lsp_call_id=*/ nullptr , LspNotify, " method" );
602+ }
603+ static constexpr llvm::StringLiteral LspReply = " [[@LSP-REPLY:" ;
604+ if (keyword.starts_with (LspReply)) {
605+ return ReplaceLspKeywordAt (content, keyword_pos,
606+ /* lsp_call_id=*/ nullptr , LspReply, " id" );
592607 }
593608
594609 return ErrorBuilder () << " Unexpected use of `[[@` at `"
@@ -625,11 +640,11 @@ static auto ReplaceContentKeywords(llvm::StringRef filename,
625640 test_name.consume_front (" todo_" );
626641
627642 // A counter for LSP calls.
628- int lsp_id = 0 ;
643+ int lsp_call_id = 0 ;
629644 while (keyword_pos != std::string::npos) {
630645 CARBON_ASSIGN_OR_RETURN (
631646 auto keyword_end,
632- ReplaceContentKeywordAt (content, keyword_pos, test_name, &lsp_id ));
647+ ReplaceContentKeywordAt (content, keyword_pos, test_name, &lsp_call_id ));
633648 keyword_pos = content->find (Prefix, keyword_end);
634649 }
635650 return Success ();
0 commit comments