[formatting] Add textDocument/rangeFormatting

This commit is contained in:
Fangrui Song 2017-12-31 19:33:28 -08:00
parent aca8e42033
commit 12ad0303be
6 changed files with 116 additions and 65 deletions

View File

@ -1,25 +1,16 @@
#if USE_CLANG_CXX #if USE_CLANG_CXX
#include "clang_format.h" #include "clang_format.h"
#include "working_files.h"
#include <loguru.hpp> #include <loguru.hpp>
using namespace clang::format; using namespace clang::format;
using namespace clang::tooling; using namespace clang::tooling;
ClangFormat::ClangFormat(llvm::StringRef document_filename, namespace {
llvm::StringRef document,
llvm::ArrayRef<clang::tooling::Range> 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(){};
static FormatStyle::LanguageKind getLanguageKindFromFilename( FormatStyle::LanguageKind getLanguageKindFromFilename(
llvm::StringRef filename) { llvm::StringRef filename) {
if (filename.endswith(".m") || filename.endswith(".mm")) { if (filename.endswith(".m") || filename.endswith(".mm")) {
return FormatStyle::LK_ObjC; return FormatStyle::LK_ObjC;
@ -27,23 +18,32 @@ static FormatStyle::LanguageKind getLanguageKindFromFilename(
return FormatStyle::LK_Cpp; return FormatStyle::LK_Cpp;
} }
std::vector<Replacement> ClangFormat::FormatWholeDocument() { } // namespace
const auto language_kind = getLanguageKindFromFilename(document_filename_);
std::vector<Replacement> ClangFormatDocument(WorkingFile* working_file,
int start,
int end,
lsFormattingOptions options) {
const auto language_kind =
getLanguageKindFromFilename(working_file->filename);
FormatStyle predefined_style; FormatStyle predefined_style;
getPredefinedStyle("chromium", language_kind, &predefined_style); getPredefinedStyle("chromium", language_kind, &predefined_style);
llvm::Expected<FormatStyle> style = llvm::Expected<FormatStyle> style =
getStyle("file", document_filename_, "chromium"); getStyle("file", working_file->filename, "chromium");
if (!style) { if (!style) {
// If, for some reason, we cannot get a format style, use Chromium's with // If, for some reason, we cannot get a format style, use Chromium's with
// tab configuration provided by the client editor. // tab configuration provided by the client editor.
LOG_S(ERROR) << llvm::toString(style.takeError()); LOG_S(ERROR) << llvm::toString(style.takeError());
predefined_style.UseTab = insert_spaces_ predefined_style.UseTab = options.insertSpaces
? FormatStyle::UseTabStyle::UT_Never ? FormatStyle::UseTabStyle::UT_Never
: FormatStyle::UseTabStyle::UT_Always; : 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>(clang::tooling::Range(start, end)),
working_file->filename);
return std::vector<Replacement>(format_result.begin(), format_result.end()); return std::vector<Replacement>(format_result.begin(), format_result.end());
} }

View File

@ -1,26 +1,18 @@
#pragma once
#if USE_CLANG_CXX #if USE_CLANG_CXX
#pragma once #include "language_server_api.h"
#include "working_files.h"
#include <clang/Format/Format.h> #include <clang/Format/Format.h>
#include <vector> #include <vector>
struct ClangFormat { std::vector<clang::tooling::Replacement> ClangFormatDocument(
llvm::StringRef document_filename_; WorkingFile* working_file,
llvm::StringRef document_; int start,
llvm::ArrayRef<clang::tooling::Range> ranges_; int end,
int tab_size_; lsFormattingOptions options);
bool insert_spaces_;
ClangFormat(llvm::StringRef document_filename,
llvm::StringRef document,
llvm::ArrayRef<clang::tooling::Range> ranges,
int tab_size,
bool insert_spaces);
~ClangFormat();
std::vector<clang::tooling::Replacement> FormatWholeDocument();
};
#endif #endif

View File

@ -459,6 +459,24 @@ enum class lsDocumentHighlightKind {
}; };
MAKE_REFLECT_TYPE_PROXY(lsDocumentHighlightKind, int); 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 // A document highlight is a range inside a text document which deserves
// special attention. Usually a document highlight is visualized by changing // special attention. Usually a document highlight is visualized by changing
// the background color of its range. // the background color of its range.

View File

@ -142,6 +142,11 @@ struct InitializeHandler : BaseMessageHandler<Ipc_InitializeRequest> {
out.result.capabilities.documentSymbolProvider = true; out.result.capabilities.documentSymbolProvider = true;
out.result.capabilities.workspaceSymbolProvider = 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 = lsDocumentLinkOptions();
out.result.capabilities.documentLinkProvider->resolveProvider = false; out.result.capabilities.documentLinkProvider->resolveProvider = false;

View File

@ -6,25 +6,6 @@
#include <loguru.hpp> #include <loguru.hpp>
namespace { 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 struct Ipc_TextDocumentFormatting
: public IpcMessage<Ipc_TextDocumentFormatting> { : public IpcMessage<Ipc_TextDocumentFormatting> {
const static IpcId kIpcId = IpcId::TextDocumentFormatting; const static IpcId kIpcId = IpcId::TextDocumentFormatting;
@ -57,20 +38,14 @@ struct TextDocumentFormattingHandler
WorkingFile* working_file = WorkingFile* working_file =
working_files->GetFileByFilename(file->def->path); 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<ClangFormat>(
working_file->filename, working_file->buffer_content,
llvm::ArrayRef<clang::tooling::Range>(
clang::tooling::Range(0, working_file->buffer_content.size())),
tab_size, insert_spaces);
const auto replacements = clang_format->FormatWholeDocument();
response.result = ConvertClangReplacementsIntoTextEdits( 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 #else
LOG_S(WARNING) << "You must compile cquery with --use-clang-cxx to use " 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? // TODO: Fallback to execute the clang-format binary?
response.result = {}; response.result = {};
#endif #endif

View File

@ -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 <loguru.hpp>
namespace {
struct Ipc_TextDocumentRangeFormatting
: public IpcMessage<Ipc_TextDocumentRangeFormatting> {
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<Out_TextDocumentRangeFormatting> {
lsRequestId id;
std::vector<lsTextEdit> result;
};
MAKE_REFLECT_STRUCT(Out_TextDocumentRangeFormatting, jsonrpc, id, result);
struct TextDocumentRangeFormattingHandler
: BaseMessageHandler<Ipc_TextDocumentRangeFormatting> {
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