blob: bbc4ff0b49072c7087f67f5581b6e12238c3a0ea [file] [log] [blame]
djasper7f663602013-03-20 09:53:23 +00001//===-- clang-format/ClangFormat.cpp - Clang format tool ------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9///
10/// \file
11/// \brief This file implements a clang-format tool that automatically formats
12/// (fragments of) C++ code.
13///
14//===----------------------------------------------------------------------===//
15
16#include "clang/Basic/Diagnostic.h"
17#include "clang/Basic/DiagnosticOptions.h"
18#include "clang/Basic/FileManager.h"
19#include "clang/Basic/SourceManager.h"
20#include "clang/Format/Format.h"
21#include "clang/Lex/Lexer.h"
22#include "clang/Rewrite/Core/Rewriter.h"
23#include "llvm/Support/FileSystem.h"
24#include "llvm/Support/Signals.h"
25
26using namespace llvm;
27
28static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
29
30static cl::list<int> Offsets(
31 "offset", cl::desc("Format a range starting at this file offset."));
32static cl::list<int> Lengths(
33 "length", cl::desc("Format a range of this length, -1 for end of file."));
34static cl::opt<std::string> Style(
35 "style",
36 cl::desc("Coding style, currently supports: LLVM, Google, Chromium."),
37 cl::init("LLVM"));
38static cl::opt<bool> Inplace("i",
39 cl::desc("Inplace edit <file>, if specified."));
40
41static cl::opt<bool> OutputXML(
42 "output-replacements-xml", cl::desc("Output replacements as XML."));
43
44static cl::opt<std::string> FileName(cl::Positional, cl::desc("[<file>]"),
45 cl::init("-"));
46
47namespace clang {
48namespace format {
49
50static FileID createInMemoryFile(StringRef FileName, const MemoryBuffer *Source,
51 SourceManager &Sources, FileManager &Files) {
52 const FileEntry *Entry = Files.getVirtualFile(FileName == "-" ? "<stdin>" :
53 FileName,
54 Source->getBufferSize(), 0);
55 Sources.overrideFileContents(Entry, Source, true);
56 return Sources.createFileID(Entry, SourceLocation(), SrcMgr::C_User);
57}
58
59static FormatStyle getStyle() {
60 FormatStyle TheStyle = getGoogleStyle();
61 if (Style == "LLVM")
62 TheStyle = getLLVMStyle();
63 if (Style == "Chromium")
64 TheStyle = getChromiumStyle();
65 return TheStyle;
66}
67
68static void format() {
69 FileManager Files((FileSystemOptions()));
70 DiagnosticsEngine Diagnostics(
71 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
72 new DiagnosticOptions);
73 SourceManager Sources(Diagnostics, Files);
74 OwningPtr<MemoryBuffer> Code;
75 if (error_code ec = MemoryBuffer::getFileOrSTDIN(FileName, Code)) {
76 llvm::errs() << ec.message() << "\n";
77 return;
78 }
79 FileID ID = createInMemoryFile(FileName, Code.get(), Sources, Files);
80 Lexer Lex(ID, Sources.getBuffer(ID), Sources, getFormattingLangOpts());
81 if (Offsets.empty())
82 Offsets.push_back(0);
83 if (Offsets.size() != Lengths.size() &&
84 !(Offsets.size() == 1 && Lengths.empty())) {
85 llvm::errs() << "Number of -offset and -length arguments must match.\n";
86 return;
87 }
88 std::vector<CharSourceRange> Ranges;
89 for (cl::list<int>::size_type i = 0, e = Offsets.size(); i != e; ++i) {
90 SourceLocation Start =
91 Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]);
92 SourceLocation End;
93 if (i < Lengths.size()) {
94 End = Start.getLocWithOffset(Lengths[i]);
95 } else {
96 End = Sources.getLocForEndOfFile(ID);
97 }
98 Ranges.push_back(CharSourceRange::getCharRange(Start, End));
99 }
100 tooling::Replacements Replaces = reformat(getStyle(), Lex, Sources, Ranges);
101 if (OutputXML) {
102 llvm::outs() << "<?xml version='1.0'?>\n<replacements xml:space='preserve'>\n";
103 for (tooling::Replacements::const_iterator I = Replaces.begin(),
104 E = Replaces.end();
105 I != E; ++I) {
106 llvm::outs() << "<replacement "
107 << "offset='" << I->getOffset() << "' "
108 << "length='" << I->getLength() << "'>"
109 << I->getReplacementText() << "</replacement>\n";
110 }
111 llvm::outs() << "</replacements>\n";
112 } else {
113 Rewriter Rewrite(Sources, LangOptions());
114 tooling::applyAllReplacements(Replaces, Rewrite);
115 if (Inplace) {
116 if (Replaces.size() == 0)
117 return; // Nothing changed, don't touch the file.
118
119 std::string ErrorInfo;
120 llvm::raw_fd_ostream FileStream(FileName.c_str(), ErrorInfo,
121 llvm::raw_fd_ostream::F_Binary);
122 if (!ErrorInfo.empty()) {
123 llvm::errs() << "Error while writing file: " << ErrorInfo << "\n";
124 return;
125 }
126 Rewrite.getEditBuffer(ID).write(FileStream);
127 FileStream.flush();
128 } else {
129 Rewrite.getEditBuffer(ID).write(outs());
130 }
131 }
132}
133
134} // namespace format
135} // namespace clang
136
137int main(int argc, const char **argv) {
138 llvm::sys::PrintStackTraceOnErrorSignal();
139 cl::ParseCommandLineOptions(
140 argc, argv,
141 "A tool to format C/C++/Obj-C code.\n\n"
djasper7f663602013-03-20 09:53:23 +0000142 "If no arguments are specified, it formats the code from standard input\n"
143 "and writes the result to the standard output.\n"
144 "If <file> is given, it reformats the file. If -i is specified together\n"
145 "with <file>, the file is edited in-place. Otherwise, the result is\n"
146 "written to the standard output.\n");
147 if (Help)
148 cl::PrintHelpMessage();
149 clang::format::format();
150 return 0;
151}