diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -3316,6 +3316,28 @@ return cursor +class Rewriter(ClangObject): + + @staticmethod + def create(tu): + return Rewriter(conf.lib.clang_CXRewriter_create(tu)) + + @property + def contents(self): + return conf.lib.clang_CXRewriter_getMainFileContents(self) + + def insertBefore(self, loc, text, indent=False): + conf.lib.clang_CXRewriter_insertTextBefore(self, loc, text, indent) + + def insertAfter(self, loc, text, indent=False): + conf.lib.clang_CXRewriter_insertTextAfter(self, loc, text, indent) + + def removeText(self, loc): + conf.lib.clang_CXRewriter_removeText(self, loc) + + def __del__(self): + conf.lib.clang_CXRewriter_dispose(self) + # Now comes the plumbing to hook up the C library. # Register callback types in common container. @@ -3441,6 +3463,27 @@ [Cursor], bool), + ("clang_CXRewriter_create", + [TranslationUnit], + c_object_p), + + ("clang_CXRewriter_dispose", + [Rewriter]), + + ("clang_CXRewriter_getMainFileContents", + [Rewriter], + _CXString, + _CXString.from_result), + + ("clang_CXRewriter_insertTextBefore", + [Rewriter, SourceLocation, c_interop_string, c_bool]), + + ("clang_CXRewriter_insertTextAfter", + [Rewriter, SourceLocation, c_interop_string, c_bool]), + + ("clang_CXRewriter_removeText", + [Rewriter, SourceRange]), + ("clang_EnumDecl_isScoped", [Cursor], bool), diff --git a/clang/bindings/python/tests/cindex/test_rewriter.py b/clang/bindings/python/tests/cindex/test_rewriter.py new file mode 100644 --- /dev/null +++ b/clang/bindings/python/tests/cindex/test_rewriter.py @@ -0,0 +1,57 @@ +import os +from clang.cindex import Config +if 'CLANG_LIBRARY_PATH' in os.environ: + Config.set_library_path(os.environ['CLANG_LIBRARY_PATH']) + +from clang.cindex import Rewriter +from clang.cindex import TranslationUnit +from clang.cindex import SourceLocation +from clang.cindex import SourceRange +import unittest +from .util import get_cursor + +kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS') + +kNewMainBody = """\ +{ + printf("dag wereld\\n"); + return 0; +}""" + +kHelloFileRewritten = """\ +// This is an auto-generated comment + +#include "stdio.h" + +int main(int argc, char* argv[]) { + printf("dag wereld\\n"); + return 0; +} +""" + +class TestRewrite(unittest.TestCase): + def test_create(self): + path = os.path.join(kInputsDir, 'hello.cpp') + tu = TranslationUnit.from_source(path) + rewiter = Rewriter.create(tu) + + def test_get_contents_no_rewrite(self): + path = os.path.join(kInputsDir, 'hello.cpp') + tu = TranslationUnit.from_source(path) + rewriter = Rewriter.create(tu) + with open(path, 'r') as f: + text = f.read() + self.assertEqual(rewriter.contents, text) + + def test_insert_remove(self): + inputPath = os.path.join(kInputsDir, 'hello.cpp') + outputPath = os.path.join(kInputsDir, 'hello_rewritten.cpp') + tu = TranslationUnit.from_source(inputPath) + rewriter = Rewriter.create(tu) + rewriter.insertAfter(tu.cursor.extent.start, '// This is an auto-generated comment\n\n') + main = get_cursor(tu, 'main') + main_xs = list(main.get_children()) + rewriter.removeText(main_xs[2].extent) + rewriter.insertAfter(main_xs[2].extent.start, kNewMainBody) + self.assertEqual(rewriter.contents, kHelloFileRewritten) + diff --git a/clang/include/clang-c/Rewrite.h b/clang/include/clang-c/Rewrite.h --- a/clang/include/clang-c/Rewrite.h +++ b/clang/include/clang-c/Rewrite.h @@ -25,22 +25,40 @@ CINDEX_LINKAGE CXRewriter clang_CXRewriter_create(CXTranslationUnit TU); /** - * Insert the specified string at the specified location in the original buffer. + * Get the contents of the main file as a string. */ -CINDEX_LINKAGE void clang_CXRewriter_insertTextBefore(CXRewriter Rew, CXSourceLocation Loc, - const char *Insert); +CXString clang_CXRewriter_getMainFileContents(CXRewriter Rew); + +/** + * Insert the given string at the specified location in the original buffer. + */ +CINDEX_LINKAGE void clang_CXRewriter_insertTextBefore(CXRewriter Rew, + CXSourceLocation Loc, + const char *Insert, + bool IndentNewLines); + +/** + * Insert the given string right after the specified location in the original + * buffer. + */ +CINDEX_LINKAGE void clang_CXRewriter_insertTextAfter(CXRewriter Rew, + CXSourceLocation Loc, + const char *Insert, + bool IndentNewLines); /** * Replace the specified range of characters in the input with the specified * replacement. */ -CINDEX_LINKAGE void clang_CXRewriter_replaceText(CXRewriter Rew, CXSourceRange ToBeReplaced, - const char *Replacement); +CINDEX_LINKAGE void clang_CXRewriter_replaceText(CXRewriter Rew, + CXSourceRange ToBeReplaced, + const char *Replacement); /** * Remove the specified range. */ -CINDEX_LINKAGE void clang_CXRewriter_removeText(CXRewriter Rew, CXSourceRange ToBeRemoved); +CINDEX_LINKAGE void clang_CXRewriter_removeText(CXRewriter Rew, + CXSourceRange ToBeRemoved); /** * Save all changed files to disk. diff --git a/clang/tools/libclang/Rewrite.cpp b/clang/tools/libclang/Rewrite.cpp --- a/clang/tools/libclang/Rewrite.cpp +++ b/clang/tools/libclang/Rewrite.cpp @@ -25,14 +25,24 @@ } void clang_CXRewriter_insertTextBefore(CXRewriter Rew, CXSourceLocation Loc, - const char *Insert) { + const char *Insert, + bool IndentNewLines) { assert(Rew); clang::Rewriter &R = *reinterpret_cast(Rew); - R.InsertTextBefore(clang::cxloc::translateSourceLocation(Loc), Insert); + R.InsertText(clang::cxloc::translateSourceLocation(Loc), Insert, false, + IndentNewLines); +} + +void clang_CXRewriter_insertTextAfter(CXRewriter Rew, CXSourceLocation Loc, + const char *Insert, bool IndentNewLines) { + assert(Rew); + clang::Rewriter &R = *reinterpret_cast(Rew); + R.InsertText(clang::cxloc::translateSourceLocation(Loc), Insert, true, + IndentNewLines); } void clang_CXRewriter_replaceText(CXRewriter Rew, CXSourceRange ToBeReplaced, - const char *Replacement) { + const char *Replacement) { assert(Rew); clang::Rewriter &R = *reinterpret_cast(Rew); R.ReplaceText(clang::cxloc::translateCXRangeToCharRange(ToBeReplaced), @@ -51,6 +61,15 @@ return R.overwriteChangedFiles(); } +CXString clang_CXRewriter_getMainFileContents(CXRewriter Rew) { + assert(Rew); + clang::Rewriter &R = *reinterpret_cast(Rew); + std::string Res; + llvm::raw_string_ostream Out(Res); + R.getEditBuffer(R.getSourceMgr().getMainFileID()).write(Out); + return clang::cxstring::createDup(Res); +} + void clang_CXRewriter_writeMainFileToStdOut(CXRewriter Rew) { assert(Rew); clang::Rewriter &R = *reinterpret_cast(Rew); diff --git a/clang/tools/libclang/libclang.exports b/clang/tools/libclang/libclang.exports --- a/clang/tools/libclang/libclang.exports +++ b/clang/tools/libclang/libclang.exports @@ -386,7 +386,9 @@ clang_Cursor_hasVarDeclGlobalStorage clang_Cursor_hasVarDeclExternalStorage clang_CXRewriter_create +clang_CXRewriter_getMainFileContents clang_CXRewriter_insertTextBefore +clang_CXRewriter_insertTextAfter clang_CXRewriter_replaceText clang_CXRewriter_removeText clang_CXRewriter_overwriteChangedFiles