diff --git a/src/clang_format.cc b/src/clang_format.cc index 1746161f..f6b95a77 100644 --- a/src/clang_format.cc +++ b/src/clang_format.cc @@ -1,25 +1,16 @@ #if USE_CLANG_CXX #include "clang_format.h" +#include "working_files.h" #include using namespace clang::format; using namespace clang::tooling; -ClangFormat::ClangFormat(llvm::StringRef document_filename, - llvm::StringRef document, - llvm::ArrayRef ranges, - int tab_size, - bool insert_spaces) - : document_filename_(document_filename), - document_(document), - ranges_(ranges), - tab_size_(tab_size), - insert_spaces_(insert_spaces) {} -ClangFormat::~ClangFormat(){}; +namespace { -static FormatStyle::LanguageKind getLanguageKindFromFilename( +FormatStyle::LanguageKind getLanguageKindFromFilename( llvm::StringRef filename) { if (filename.endswith(".m") || filename.endswith(".mm")) { return FormatStyle::LK_ObjC; @@ -27,23 +18,32 @@ static FormatStyle::LanguageKind getLanguageKindFromFilename( return FormatStyle::LK_Cpp; } -std::vector ClangFormat::FormatWholeDocument() { - const auto language_kind = getLanguageKindFromFilename(document_filename_); +} // namespace + +std::vector ClangFormatDocument(WorkingFile* working_file, + int start, + int end, + lsFormattingOptions options) { + const auto language_kind = + getLanguageKindFromFilename(working_file->filename); FormatStyle predefined_style; getPredefinedStyle("chromium", language_kind, &predefined_style); llvm::Expected style = - getStyle("file", document_filename_, "chromium"); + getStyle("file", working_file->filename, "chromium"); if (!style) { // If, for some reason, we cannot get a format style, use Chromium's with // tab configuration provided by the client editor. LOG_S(ERROR) << llvm::toString(style.takeError()); - predefined_style.UseTab = insert_spaces_ + predefined_style.UseTab = options.insertSpaces ? FormatStyle::UseTabStyle::UT_Never : FormatStyle::UseTabStyle::UT_Always; - predefined_style.IndentWidth = tab_size_; + predefined_style.IndentWidth = options.tabSize; } - auto format_result = reformat(*style, document_, ranges_, document_filename_); + auto format_result = reformat( + *style, working_file->buffer_content, + llvm::ArrayRef(clang::tooling::Range(start, end)), + working_file->filename); return std::vector(format_result.begin(), format_result.end()); } diff --git a/src/clang_format.h b/src/clang_format.h index dc7b4565..1a0b359d 100644 --- a/src/clang_format.h +++ b/src/clang_format.h @@ -1,26 +1,18 @@ +#pragma once + #if USE_CLANG_CXX -#pragma once +#include "language_server_api.h" +#include "working_files.h" #include #include -struct ClangFormat { - llvm::StringRef document_filename_; - llvm::StringRef document_; - llvm::ArrayRef ranges_; - int tab_size_; - bool insert_spaces_; - - ClangFormat(llvm::StringRef document_filename, - llvm::StringRef document, - llvm::ArrayRef ranges, - int tab_size, - bool insert_spaces); - ~ClangFormat(); - - std::vector FormatWholeDocument(); -}; +std::vector ClangFormatDocument( + WorkingFile* working_file, + int start, + int end, + lsFormattingOptions options); #endif diff --git a/src/language_server_api.h b/src/language_server_api.h index cd139ede..289df238 100644 --- a/src/language_server_api.h +++ b/src/language_server_api.h @@ -459,6 +459,24 @@ enum class lsDocumentHighlightKind { }; MAKE_REFLECT_TYPE_PROXY(lsDocumentHighlightKind, int); + +struct lsFormattingOptions { + // Size of a tab in spaces. + int tabSize; + // Prefer spaces over tabs. + bool insertSpaces; +}; +MAKE_REFLECT_STRUCT(lsFormattingOptions, tabSize, insertSpaces); + +struct lsTextDocumentFormattingParams { + // The text document. + lsTextDocumentIdentifier textDocument; + + // The format options, like tabs or spaces. + lsFormattingOptions options; +}; +MAKE_REFLECT_STRUCT(lsTextDocumentFormattingParams, textDocument, options); + // A document highlight is a range inside a text document which deserves // special attention. Usually a document highlight is visualized by changing // the background color of its range. diff --git a/src/messages/initialize.cc b/src/messages/initialize.cc index fdb51729..1486937e 100644 --- a/src/messages/initialize.cc +++ b/src/messages/initialize.cc @@ -142,6 +142,11 @@ struct InitializeHandler : BaseMessageHandler { out.result.capabilities.documentSymbolProvider = true; out.result.capabilities.workspaceSymbolProvider = true; +#if USE_CLANG_CXX + out.result.capabilities.documentFormattingProvider = true; + out.result.capabilities.documentRangeFormattingProvider = true; +#endif + out.result.capabilities.documentLinkProvider = lsDocumentLinkOptions(); out.result.capabilities.documentLinkProvider->resolveProvider = false; diff --git a/src/messages/text_document_formatting.cc b/src/messages/text_document_formatting.cc index 343e9448..5ef978d7 100644 --- a/src/messages/text_document_formatting.cc +++ b/src/messages/text_document_formatting.cc @@ -6,25 +6,6 @@ #include namespace { -struct lsFormattingOptions { - // Size of a tab in spaces. - int tabSize; - // Prefer spaces over tabs. - bool insertSpaces; -}; -MAKE_REFLECT_STRUCT(lsFormattingOptions, tabSize, insertSpaces); - -struct lsTextDocumentFormattingParams { - // The text document. - lsTextDocumentIdentifier textDocument; - - // The format options, like tabs or spaces. - lsFormattingOptions options; -}; -MAKE_REFLECT_STRUCT(lsTextDocumentFormattingParams, - textDocument, - options); - struct Ipc_TextDocumentFormatting : public IpcMessage { const static IpcId kIpcId = IpcId::TextDocumentFormatting; @@ -57,20 +38,14 @@ struct TextDocumentFormattingHandler WorkingFile* working_file = working_files->GetFileByFilename(file->def->path); - int tab_size = request->params.options.tabSize; - bool insert_spaces = request->params.options.insertSpaces; - - const auto clang_format = MakeUnique( - working_file->filename, working_file->buffer_content, - llvm::ArrayRef( - clang::tooling::Range(0, working_file->buffer_content.size())), - tab_size, insert_spaces); - const auto replacements = clang_format->FormatWholeDocument(); response.result = ConvertClangReplacementsIntoTextEdits( - working_file->buffer_content, replacements); + working_file->buffer_content, + ClangFormatDocument(working_file, 0, + working_file->buffer_content.size(), + request->params.options)); #else LOG_S(WARNING) << "You must compile cquery with --use-clang-cxx to use " - "document formatting."; + "textDocument/formatting."; // TODO: Fallback to execute the clang-format binary? response.result = {}; #endif diff --git a/src/messages/text_document_range_formatting.cc b/src/messages/text_document_range_formatting.cc new file mode 100644 index 00000000..01d78a63 --- /dev/null +++ b/src/messages/text_document_range_formatting.cc @@ -0,0 +1,61 @@ +#include "clang_format.h" +#include "lex_utils.h" +#include "message_handler.h" +#include "queue_manager.h" +#include "working_files.h" + +#include + +namespace { +struct Ipc_TextDocumentRangeFormatting + : public IpcMessage { + const static IpcId kIpcId = IpcId::TextDocumentRangeFormatting; + + lsRequestId id; + lsRange range; + lsTextDocumentFormattingParams params; +}; +MAKE_REFLECT_STRUCT(Ipc_TextDocumentRangeFormatting, id, range, params); +REGISTER_IPC_MESSAGE(Ipc_TextDocumentRangeFormatting); + +struct Out_TextDocumentRangeFormatting + : public lsOutMessage { + lsRequestId id; + std::vector result; +}; +MAKE_REFLECT_STRUCT(Out_TextDocumentRangeFormatting, jsonrpc, id, result); + +struct TextDocumentRangeFormattingHandler + : BaseMessageHandler { + void Run(Ipc_TextDocumentRangeFormatting* request) override { + Out_TextDocumentRangeFormatting response; + response.id = request->id; +#if USE_CLANG_CXX + QueryFile* file; + if (!FindFileOrFail(db, project, request->id, + request->params.textDocument.uri.GetPath(), &file)) { + return; + } + + WorkingFile* working_file = + working_files->GetFileByFilename(file->def->path); + + int start = GetOffsetForPosition(request->range.start, + working_file->buffer_content), + end = GetOffsetForPosition(request->range.end, + working_file->buffer_content); + response.result = ConvertClangReplacementsIntoTextEdits( + working_file->buffer_content, + ClangFormatDocument(working_file, start, end, request->params.options)); +#else + LOG_S(WARNING) << "You must compile cquery with --use-clang-cxx to use " + "textDocument/rangeFormatting."; + // TODO: Fallback to execute the clang-format binary? + response.result = {}; +#endif + + QueueManager::WriteStdout(IpcId::TextDocumentRangeFormatting, response); + } +}; +REGISTER_MESSAGE_HANDLER(TextDocumentRangeFormattingHandler); +} // namespace