r182373 - Let clang-format move the cursor appropriately.

Daniel Jasper djasper at google.com
Tue May 21 05:21:39 PDT 2013


Author: djasper
Date: Tue May 21 07:21:39 2013
New Revision: 182373

URL: http://llvm.org/viewvc/llvm-project?rev=182373&view=rev
Log:
Let clang-format move the cursor appropriately.

With this patch, clang-format will try to keep the cursor at the
original code position in editor integrations (implemented for emacs and
vim). This means, after formatting, clang-format will try to keep the
cursor on the same character of the same token.

Added:
    cfe/trunk/test/Format/cursor.cpp
Modified:
    cfe/trunk/include/clang/Tooling/Refactoring.h
    cfe/trunk/lib/Tooling/Refactoring.cpp
    cfe/trunk/tools/clang-format/ClangFormat.cpp
    cfe/trunk/tools/clang-format/clang-format.el
    cfe/trunk/tools/clang-format/clang-format.py
    cfe/trunk/unittests/Tooling/RefactoringTest.cpp

Modified: cfe/trunk/include/clang/Tooling/Refactoring.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring.h?rev=182373&r1=182372&r2=182373&view=diff
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring.h (original)
+++ cfe/trunk/include/clang/Tooling/Refactoring.h Tue May 21 07:21:39 2013
@@ -132,6 +132,10 @@ bool applyAllReplacements(Replacements &
 /// replacements cannot be applied, this returns an empty \c string.
 std::string applyAllReplacements(StringRef Code, Replacements &Replaces);
 
+/// \brief Calculates how a code \p Position is shifted when \p Replaces are
+/// applied.
+unsigned shiftedCodePosition(const Replacements& Replaces, unsigned Position);
+
 /// \brief A tool to run refactorings.
 ///
 /// This is a refactoring specific version of \see ClangTool. FrontendActions

Modified: cfe/trunk/lib/Tooling/Refactoring.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring.cpp?rev=182373&r1=182372&r2=182373&view=diff
==============================================================================
--- cfe/trunk/lib/Tooling/Refactoring.cpp (original)
+++ cfe/trunk/lib/Tooling/Refactoring.cpp Tue May 21 07:21:39 2013
@@ -166,6 +166,19 @@ std::string applyAllReplacements(StringR
   return Result;
 }
 
+unsigned shiftedCodePosition(const Replacements &Replaces, unsigned Position) {
+  unsigned NewPosition = Position;
+  for (Replacements::iterator I = Replaces.begin(), E = Replaces.end(); I != E;
+       ++I) {
+    if (I->getOffset() >= Position)
+      break;
+    if (I->getOffset() + I->getLength() > Position)
+      NewPosition += I->getOffset() + I->getLength() - Position;
+    NewPosition += I->getReplacementText().size() - I->getLength();
+  }
+  return NewPosition;
+}
+
 RefactoringTool::RefactoringTool(const CompilationDatabase &Compilations,
                                  ArrayRef<std::string> SourcePaths)
   : ClangTool(Compilations, SourcePaths) {}

Added: cfe/trunk/test/Format/cursor.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Format/cursor.cpp?rev=182373&view=auto
==============================================================================
--- cfe/trunk/test/Format/cursor.cpp (added)
+++ cfe/trunk/test/Format/cursor.cpp Tue May 21 07:21:39 2013
@@ -0,0 +1,6 @@
+// RUN: grep -Ev "// *[A-Z-]+:" %s > %t2.cpp
+// RUN: clang-format -style=LLVM %t2.cpp -cursor=6 > %t.cpp
+// RUN: FileCheck -strict-whitespace -input-file=%t.cpp %s
+// CHECK: {{^\{ "Cursor": 4 \}$}}
+// CHECK: {{^int\ \i;$}}
+ int    i;

Modified: cfe/trunk/tools/clang-format/ClangFormat.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/clang-format/ClangFormat.cpp?rev=182373&r1=182372&r2=182373&view=diff
==============================================================================
--- cfe/trunk/tools/clang-format/ClangFormat.cpp (original)
+++ cfe/trunk/tools/clang-format/ClangFormat.cpp Tue May 21 07:21:39 2013
@@ -77,6 +77,11 @@ static cl::opt<bool>
                cl::desc("Dump configuration options to stdout and exit.\n"
                         "Can be used with -style option."),
                cl::cat(ClangFormatCategory));
+static cl::opt<unsigned>
+    Cursor("cursor",
+           cl::desc("The position of the cursor when invoking clang-format from"
+                    " an editor integration"),
+           cl::init(0), cl::cat(ClangFormatCategory));
 
 static cl::list<std::string> FileNames(cl::Positional, cl::desc("[<file> ...]"),
                                        cl::cat(ClangFormatCategory));
@@ -221,6 +226,9 @@ static bool format(std::string FileName)
       Rewrite.getEditBuffer(ID).write(FileStream);
       FileStream.flush();
     } else {
+      if (Cursor != 0)
+        outs() << "{ \"Cursor\": " << tooling::shiftedCodePosition(
+                                          Replaces, Cursor) << " }\n";
       Rewrite.getEditBuffer(ID).write(outs());
     }
   }

Modified: cfe/trunk/tools/clang-format/clang-format.el
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/clang-format/clang-format.el?rev=182373&r1=182372&r2=182373&view=diff
==============================================================================
--- cfe/trunk/tools/clang-format/clang-format.el (original)
+++ cfe/trunk/tools/clang-format/clang-format.el Tue May 21 07:21:39 2013
@@ -9,6 +9,8 @@
 ;; Depending on your configuration and coding style, you might need to modify
 ;; 'style' in clang-format, below.
 
+(require 'json)
+
 ;; *Location of the clang-format binary. If it is on your PATH, a full path name
 ;; need not be specified.
 (defvar clang-format-binary "clang-format")
@@ -38,8 +40,14 @@
         (call-process-region (point-min) (point-max) clang-format-binary t t nil
                              "-offset" (number-to-string (1- begin))
                              "-length" (number-to-string (- end begin))
+                             "-cursor" (number-to-string (point))
                              "-style" style)
-      (goto-char orig-point)
-      (dotimes (index (length orig-windows))
-        (set-window-start (nth index orig-windows)
-                          (nth index orig-window-starts))))))
+      (goto-char (point-min))
+      (let ((json-output (json-read-from-string
+                           (buffer-substring-no-properties
+                             (point-min) (line-beginning-position 2)))))
+        (delete-region (point-min) (line-beginning-position 2))
+        (goto-char (cdr (assoc 'Cursor json-output)))
+        (dotimes (index (length orig-windows))
+          (set-window-start (nth index orig-windows)
+                            (nth index orig-window-starts)))))))

Modified: cfe/trunk/tools/clang-format/clang-format.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/clang-format/clang-format.py?rev=182373&r1=182372&r2=182373&view=diff
==============================================================================
--- cfe/trunk/tools/clang-format/clang-format.py (original)
+++ cfe/trunk/tools/clang-format/clang-format.py Tue May 21 07:21:39 2013
@@ -17,8 +17,9 @@
 # It operates on the current, potentially unsaved buffer and does not create
 # or save any files. To revert a formatting, just undo.
 
-import vim
+import json
 import subprocess
+import vim
 
 # Change this to the full path if clang-format is not on the path.
 binary = 'clang-format'
@@ -29,9 +30,10 @@ style = 'LLVM'
 
 # Get the current text.
 buf = vim.current.buffer
-text = "\n".join(buf)
+text = '\n'.join(buf)
 
 # Determine range to format.
+cursor = int(vim.eval('line2byte(line("."))+col(".")')) - 2
 offset = int(vim.eval('line2byte(' +
                       str(vim.current.range.start + 1) + ')')) - 1
 length = int(vim.eval('line2byte(' +
@@ -39,7 +41,7 @@ length = int(vim.eval('line2byte(' +
 
 # Call formatter.
 p = subprocess.Popen([binary, '-offset', str(offset), '-length', str(length),
-                      '-style', style],
+                      '-style', style, '-cursor', str(cursor)],
                      stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                      stdin=subprocess.PIPE)
 stdout, stderr = p.communicate(input=text)
@@ -56,10 +58,14 @@ if stderr:
 if not stdout:
   print ('No output from clang-format (crashed?).\n' +
       'Please report to bugs.llvm.org.')
-elif stdout != text:
+else:
   lines = stdout.split('\n')
-  for i in range(min(len(buf), len(lines))):
-    buf[i] = lines[i]
-  for line in lines[len(buf):]:
-    buf.append(line)
-  del buf[len(lines):]
+  output = json.loads(lines[0])
+  lines = lines[1:]
+  if '\n'.join(lines) != text:
+    for i in range(min(len(buf), len(lines))):
+      buf[i] = lines[i]
+    for line in lines[len(buf):]:
+      buf.append(line)
+    del buf[len(lines):]
+    vim.command('goto %d' % (output['Cursor'] + 1))

Modified: cfe/trunk/unittests/Tooling/RefactoringTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/RefactoringTest.cpp?rev=182373&r1=182372&r2=182373&view=diff
==============================================================================
--- cfe/trunk/unittests/Tooling/RefactoringTest.cpp (original)
+++ cfe/trunk/unittests/Tooling/RefactoringTest.cpp Tue May 21 07:21:39 2013
@@ -151,6 +151,30 @@ TEST_F(ReplacementTest, ApplyAllFailsIfO
   EXPECT_EQ("z", Context.getRewrittenText(IDz));
 }
 
+TEST(ShiftedCodePositionTest, FindsNewCodePosition) {
+  Replacements Replaces;
+  Replaces.insert(Replacement("", 0, 1, ""));
+  Replaces.insert(Replacement("", 4, 3, " "));
+  // Assume ' int   i;' is turned into 'int i;' and cursor is located at '|'.
+  EXPECT_EQ(0u, shiftedCodePosition(Replaces, 0)); // |int   i;
+  EXPECT_EQ(0u, shiftedCodePosition(Replaces, 1)); //  |nt   i;
+  EXPECT_EQ(1u, shiftedCodePosition(Replaces, 2)); //  i|t   i;
+  EXPECT_EQ(2u, shiftedCodePosition(Replaces, 3)); //  in|   i;
+  EXPECT_EQ(3u, shiftedCodePosition(Replaces, 4)); //  int|  i;
+  EXPECT_EQ(4u, shiftedCodePosition(Replaces, 5)); //  int | i;
+  EXPECT_EQ(4u, shiftedCodePosition(Replaces, 6)); //  int  |i;
+  EXPECT_EQ(4u, shiftedCodePosition(Replaces, 7)); //  int   |;
+  EXPECT_EQ(5u, shiftedCodePosition(Replaces, 8)); //  int   i|
+}
+
+TEST(ShiftedCodePositionTest, FindsNewCodePositionWithInserts) {
+  Replacements Replaces;
+  Replaces.insert(Replacement("", 4, 0, "\"\n\""));
+  // Assume '"12345678"' is turned into '"1234"\n"5678"'.
+  EXPECT_EQ(4u, shiftedCodePosition(Replaces, 4)); // "123|5678"
+  EXPECT_EQ(8u, shiftedCodePosition(Replaces, 5)); // "1234|678"
+}
+
 class FlushRewrittenFilesTest : public ::testing::Test {
  public:
   FlushRewrittenFilesTest() {





More information about the cfe-commits mailing list