This repository has been archived by the owner on Dec 29, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathclang_format_collaborator.cc
105 lines (94 loc) · 3.26 KB
/
clang_format_collaborator.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// Copyright 2017 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/strings/str_cat.h"
#include "buffer.h"
#include "clang_config.h"
#include "log.h"
#include "run.h"
#include "src/pugixml.hpp"
class ClangFormatCollaborator final : public SyncCollaborator {
public:
ClangFormatCollaborator(const Buffer* buffer)
: SyncCollaborator("clang-format", absl::Seconds(2),
absl::Milliseconds(100)),
buffer_(buffer) {}
EditResponse Edit(const EditNotification& notification) override;
private:
const Buffer* const buffer_;
};
EditResponse ClangFormatCollaborator::Edit(
const EditNotification& notification) {
EditResponse response;
if (!notification.fully_loaded) return response;
auto str = notification.content;
auto text = str.Render();
auto clang_format = ClangToolPath(buffer_->project(), "clang-format");
Log() << "clang-format command: " << clang_format;
auto res =
run(clang_format,
{"-output-replacements-xml",
absl::StrCat("-assume-filename=", buffer_->filename().string())},
text);
Log() << res.out;
pugi::xml_document doc;
auto parse_result =
doc.load_buffer(res.out.data(), res.out.length(),
(pugi::parse_default | pugi::parse_ws_pcdata_single));
if (!parse_result) {
return response;
}
if (doc.child("replacements").attribute("incomplete_format").as_bool()) {
return response;
}
struct Replacement {
int offset;
int length;
const char* text;
};
std::vector<Replacement> replacements;
for (auto replacement : doc.child("replacements").children("replacement")) {
replacements.push_back(Replacement{replacement.attribute("offset").as_int(),
replacement.attribute("length").as_int(),
replacement.child_value()});
}
int n = 0;
AnnotatedString::Iterator it(str, AnnotatedString::Begin());
it.MoveNext();
for (auto r : replacements) {
Log() << "REPLACE: " << r.offset << "+" << r.length << " with '" << r.text
<< "' starting from " << n;
for (; n < r.offset; n++) {
it.MoveNext();
}
for (int i = 0; i < r.length; i++) {
auto del = it.id();
it.MoveNext();
n++;
str.MakeDelete(&response.content_updates, del);
}
if (r.text[0]) {
str.MakeInsert(&response.content_updates, buffer_->site(), r.text,
it.Prev().id());
}
}
return response;
}
SERVER_COLLABORATOR(ClangFormatCollaborator, buffer) {
auto fext = buffer->filename().extension();
for (auto mext : {".c", ".cxx", ".cpp", ".C", ".cc", ".h", ".H", ".hpp",
".hxx", ".proto", ".js", ".java", ".m"}) {
if (fext == mext) return true;
}
return false;
}