[PATCH] Let clang-format move the cursor appropriately.

Daniel Jasper djasper at google.com
Fri May 17 08:56:42 PDT 2013


Hi klimek,

If we format something in an editor integration, we basically want the cursor to be at the same spot relative to the changed source code.

http://llvm-reviews.chandlerc.com/D812

Files:
  test/Format/cursor.cpp
  tools/clang-format/ClangFormat.cpp
  tools/clang-format/clang-format.py

Index: test/Format/cursor.cpp
===================================================================
--- /dev/null
+++ test/Format/cursor.cpp
@@ -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;
Index: tools/clang-format/ClangFormat.cpp
===================================================================
--- tools/clang-format/ClangFormat.cpp
+++ tools/clang-format/ClangFormat.cpp
@@ -71,6 +71,11 @@
                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));
@@ -123,6 +128,20 @@
   return getLLVMStyle();
 }
 
+static unsigned updatedOffset(const tooling::Replacements &Replaces,
+                              unsigned Offset) {
+  int ShiftedBy = 0;
+  for (tooling::Replacements::iterator I = Replaces.begin(), E = Replaces.end();
+       I != E; ++I) {
+    if (I->getOffset() > Offset)
+      break;
+    if (I->getOffset() + I->getLength() > Offset)
+      ShiftedBy += I->getOffset() + I->getLength() - Offset;
+    ShiftedBy += I->getReplacementText().size() - I->getLength();
+  }
+  return Offset + ShiftedBy;
+}
+
 // Returns true on error.
 static bool format(std::string FileName) {
   FileManager Files((FileSystemOptions()));
@@ -199,6 +218,8 @@
       Rewrite.getEditBuffer(ID).write(FileStream);
       FileStream.flush();
     } else {
+      if (Cursor != 0)
+        outs() << "{ \"Cursor\": " << updatedOffset(Replaces, Cursor) << " }\n";
       Rewrite.getEditBuffer(ID).write(outs());
     }
   }
Index: tools/clang-format/clang-format.py
===================================================================
--- tools/clang-format/clang-format.py
+++ tools/clang-format/clang-format.py
@@ -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,17 +30,18 @@
 
 # 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(' +
                       str(vim.current.range.end + 2) + ')')) - offset - 2
 
 # 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 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))
-------------- next part --------------
A non-text attachment was scrubbed...
Name: D812.1.patch
Type: text/x-patch
Size: 4065 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20130517/41687ff2/attachment.bin>


More information about the cfe-commits mailing list