mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-25 17:11:59 +00:00
Merge branch 'document-formatting' of https://github.com/danielmartin/cquery into danielmartin-document-formatting
This commit is contained in:
commit
9d04446d48
50
src/clang_format.cc
Normal file
50
src/clang_format.cc
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#if USE_CLANG_CXX
|
||||||
|
|
||||||
|
#include "clang_format.h"
|
||||||
|
|
||||||
|
#include <loguru.hpp>
|
||||||
|
|
||||||
|
using namespace clang::format;
|
||||||
|
using namespace clang::tooling;
|
||||||
|
|
||||||
|
ClangFormat::ClangFormat(llvm::StringRef document_filename,
|
||||||
|
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(
|
||||||
|
llvm::StringRef filename) {
|
||||||
|
if (filename.endswith(".m") || filename.endswith(".mm")) {
|
||||||
|
return FormatStyle::LK_ObjC;
|
||||||
|
}
|
||||||
|
return FormatStyle::LK_Cpp;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Replacement> ClangFormat::FormatWholeDocument() {
|
||||||
|
const auto language_kind = getLanguageKindFromFilename(document_filename_);
|
||||||
|
FormatStyle predefined_style;
|
||||||
|
getPredefinedStyle("chromium", language_kind, &predefined_style);
|
||||||
|
llvm::Expected<FormatStyle> style =
|
||||||
|
getStyle("file", document_filename_, "chromium");
|
||||||
|
if (!style) {
|
||||||
|
LOG_S(ERROR) << llvm::toString(style.takeError());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*style == predefined_style) {
|
||||||
|
style->UseTab = insert_spaces_ ? FormatStyle::UseTabStyle::UT_Never
|
||||||
|
: FormatStyle::UseTabStyle::UT_Always;
|
||||||
|
style->IndentWidth = tab_size_;
|
||||||
|
}
|
||||||
|
auto format_result = reformat(*style, document_, ranges_, document_filename_);
|
||||||
|
return std::vector<Replacement>(format_result.begin(), format_result.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
26
src/clang_format.h
Normal file
26
src/clang_format.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#if USE_CLANG_CXX
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <clang/Format/Format.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct ClangFormat {
|
||||||
|
llvm::StringRef document_filename_;
|
||||||
|
llvm::StringRef document_;
|
||||||
|
llvm::ArrayRef<clang::tooling::Range> ranges_;
|
||||||
|
int tab_size_;
|
||||||
|
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
|
@ -104,6 +104,30 @@ optional<lsDiagnostic> BuildAndDisposeDiagnostic(CXDiagnostic diagnostic,
|
|||||||
return ls_diagnostic;
|
return ls_diagnostic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if USE_CLANG_CXX
|
||||||
|
static lsPosition OffsetToRange(llvm::StringRef document, size_t offset) {
|
||||||
|
// TODO: Support Windows line endings, etc.
|
||||||
|
llvm::StringRef text_before = document.substr(0, offset);
|
||||||
|
int num_line = text_before.count('\n');
|
||||||
|
int num_column = text_before.size() - text_before.rfind('\n') - 1;
|
||||||
|
return {num_line, num_column};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<lsTextEdit> ConvertClangReplacementsIntoTextEdits(
|
||||||
|
llvm::StringRef document,
|
||||||
|
const std::vector<clang::tooling::Replacement>& clang_replacements) {
|
||||||
|
std::vector<lsTextEdit> text_edits_result;
|
||||||
|
for (const auto& replacement : clang_replacements) {
|
||||||
|
const auto startPosition = OffsetToRange(document, replacement.getOffset());
|
||||||
|
const auto endPosition = OffsetToRange(
|
||||||
|
document, replacement.getOffset() + replacement.getLength());
|
||||||
|
text_edits_result.push_back(
|
||||||
|
{{startPosition, endPosition}, replacement.getReplacementText()});
|
||||||
|
}
|
||||||
|
return text_edits_result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
std::string FileName(CXFile file) {
|
std::string FileName(CXFile file) {
|
||||||
CXString cx_name = clang_getFileName(file);
|
CXString cx_name = clang_getFileName(file);
|
||||||
std::string name = ToString(cx_name);
|
std::string name = ToString(cx_name);
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
#include "language_server_api.h"
|
#include "language_server_api.h"
|
||||||
|
|
||||||
#include <clang-c/Index.h>
|
#include <clang-c/Index.h>
|
||||||
|
#if USE_CLANG_CXX
|
||||||
|
#include <clang/Format/Format.h>
|
||||||
|
#endif
|
||||||
#include <optional.h>
|
#include <optional.h>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -17,4 +20,11 @@ std::string FileName(CXFile file);
|
|||||||
|
|
||||||
std::string ToString(CXString cx_string);
|
std::string ToString(CXString cx_string);
|
||||||
|
|
||||||
std::string ToString(CXCursorKind cursor_kind);
|
std::string ToString(CXCursorKind cursor_kind);
|
||||||
|
|
||||||
|
// Converts Clang formatting replacement operations into LSP text edits.
|
||||||
|
#if USE_CLANG_CXX
|
||||||
|
std::vector<lsTextEdit> ConvertClangReplacementsIntoTextEdits(
|
||||||
|
llvm::StringRef document,
|
||||||
|
const std::vector<clang::tooling::Replacement>& clang_replacements);
|
||||||
|
#endif
|
||||||
|
@ -34,6 +34,8 @@ const char* IpcIdToString(IpcId id) {
|
|||||||
return "textDocument/documentHighlight";
|
return "textDocument/documentHighlight";
|
||||||
case IpcId::TextDocumentHover:
|
case IpcId::TextDocumentHover:
|
||||||
return "textDocument/hover";
|
return "textDocument/hover";
|
||||||
|
case IpcId::TextDocumentFormatting:
|
||||||
|
return "textDocument/formatting";
|
||||||
case IpcId::TextDocumentReferences:
|
case IpcId::TextDocumentReferences:
|
||||||
return "textDocument/references";
|
return "textDocument/references";
|
||||||
case IpcId::TextDocumentDocumentSymbol:
|
case IpcId::TextDocumentDocumentSymbol:
|
||||||
@ -89,4 +91,4 @@ const char* IpcIdToString(IpcId id) {
|
|||||||
|
|
||||||
BaseIpcMessage::BaseIpcMessage(IpcId method_id) : method_id(method_id) {}
|
BaseIpcMessage::BaseIpcMessage(IpcId method_id) : method_id(method_id) {}
|
||||||
|
|
||||||
BaseIpcMessage::~BaseIpcMessage() = default;
|
BaseIpcMessage::~BaseIpcMessage() = default;
|
||||||
|
@ -22,6 +22,7 @@ enum class IpcId : int {
|
|||||||
TextDocumentDefinition,
|
TextDocumentDefinition,
|
||||||
TextDocumentDocumentHighlight,
|
TextDocumentDocumentHighlight,
|
||||||
TextDocumentHover,
|
TextDocumentHover,
|
||||||
|
TextDocumentFormatting,
|
||||||
TextDocumentReferences,
|
TextDocumentReferences,
|
||||||
TextDocumentDocumentSymbol,
|
TextDocumentDocumentSymbol,
|
||||||
TextDocumentDocumentLink,
|
TextDocumentDocumentLink,
|
||||||
@ -76,4 +77,4 @@ struct BaseIpcMessage {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
struct IpcMessage : public BaseIpcMessage {
|
struct IpcMessage : public BaseIpcMessage {
|
||||||
IpcMessage() : BaseIpcMessage(T::kIpcId) {}
|
IpcMessage() : BaseIpcMessage(T::kIpcId) {}
|
||||||
};
|
};
|
||||||
|
81
src/messages/text_document_formatting.cc
Normal file
81
src/messages/text_document_formatting.cc
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#include "clang_format.h"
|
||||||
|
#include "message_handler.h"
|
||||||
|
#include "queue_manager.h"
|
||||||
|
#include "working_files.h"
|
||||||
|
|
||||||
|
#include <loguru.hpp>
|
||||||
|
|
||||||
|
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 formattingOptions;
|
||||||
|
};
|
||||||
|
MAKE_REFLECT_STRUCT(lsTextDocumentFormattingParams,
|
||||||
|
textDocument,
|
||||||
|
formattingOptions);
|
||||||
|
|
||||||
|
struct Ipc_TextDocumentFormatting
|
||||||
|
: public IpcMessage<Ipc_TextDocumentFormatting> {
|
||||||
|
const static IpcId kIpcId = IpcId::TextDocumentFormatting;
|
||||||
|
|
||||||
|
lsRequestId id;
|
||||||
|
lsTextDocumentFormattingParams params;
|
||||||
|
};
|
||||||
|
MAKE_REFLECT_STRUCT(Ipc_TextDocumentFormatting, id, params);
|
||||||
|
REGISTER_IPC_MESSAGE(Ipc_TextDocumentFormatting);
|
||||||
|
|
||||||
|
struct Out_TextDocumentFormatting
|
||||||
|
: public lsOutMessage<Out_TextDocumentFormatting> {
|
||||||
|
lsRequestId id;
|
||||||
|
std::vector<lsTextEdit> result;
|
||||||
|
};
|
||||||
|
MAKE_REFLECT_STRUCT(Out_TextDocumentFormatting, jsonrpc, id, result);
|
||||||
|
|
||||||
|
struct TextDocumentFormattingHandler
|
||||||
|
: BaseMessageHandler<Ipc_TextDocumentFormatting> {
|
||||||
|
void Run(Ipc_TextDocumentFormatting* request) override {
|
||||||
|
Out_TextDocumentFormatting 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 tab_size = request->params.formattingOptions.tabSize;
|
||||||
|
bool insert_spaces = request->params.formattingOptions.insertSpaces;
|
||||||
|
|
||||||
|
const auto clang_format = std::unique_ptr<ClangFormat>(new ClangFormat(
|
||||||
|
working_file->filename, working_file->buffer_content,
|
||||||
|
{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);
|
||||||
|
#else
|
||||||
|
LOG_S(WARNING) << "You must compile cquery with --use-clang-cxx to use "
|
||||||
|
"document formatting.";
|
||||||
|
// TODO: Fallback to execute the clang-format binary?
|
||||||
|
response.result = {};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QueueManager::WriteStdout(IpcId::TextDocumentFormatting, response);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
REGISTER_MESSAGE_HANDLER(TextDocumentFormattingHandler);
|
||||||
|
} // namespace
|
7
wscript
7
wscript
@ -56,8 +56,8 @@ if sys.version_info < (3, 0):
|
|||||||
if ret == 0:
|
if ret == 0:
|
||||||
err = ctypes.WinError()
|
err = ctypes.WinError()
|
||||||
ERROR_PRIVILEGE_NOT_HELD = 1314
|
ERROR_PRIVILEGE_NOT_HELD = 1314
|
||||||
# Creating symbolic link on Windows requires a special priviledge SeCreateSymboliclinkPrivilege,
|
# Creating symbolic link on Windows requires a special priviledge SeCreateSymboliclinkPrivilege,
|
||||||
# which an non-elevated process lacks. Starting with Windows 10 build 14972, this got relaxed
|
# which an non-elevated process lacks. Starting with Windows 10 build 14972, this got relaxed
|
||||||
# when Developer Mode is enabled. Triggering this new behaviour requires a new flag. Try again.
|
# when Developer Mode is enabled. Triggering this new behaviour requires a new flag. Try again.
|
||||||
if err[0] == ERROR_PRIVILEGE_NOT_HELD:
|
if err[0] == ERROR_PRIVILEGE_NOT_HELD:
|
||||||
flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
|
flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
|
||||||
@ -256,6 +256,9 @@ def build(bld):
|
|||||||
lib.append('clangAST')
|
lib.append('clangAST')
|
||||||
lib.append('clangLex')
|
lib.append('clangLex')
|
||||||
lib.append('clangBasic')
|
lib.append('clangBasic')
|
||||||
|
lib.append('clangFormat')
|
||||||
|
lib.append('clangToolingCore')
|
||||||
|
lib.append('clangRewrite')
|
||||||
|
|
||||||
# The order is derived from llvm-config --libs core
|
# The order is derived from llvm-config --libs core
|
||||||
lib.append('LLVMCore')
|
lib.append('LLVMCore')
|
||||||
|
Loading…
Reference in New Issue
Block a user