forked from OSchip/llvm-project
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. llvm-svn: 182373
This commit is contained in:
parent
c787d42f40
commit
2a250b8b49
|
@ -132,6 +132,10 @@ bool applyAllReplacements(Replacements &Replaces, Rewriter &Rewrite);
|
|||
/// 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
|
||||
|
|
|
@ -166,6 +166,19 @@ std::string applyAllReplacements(StringRef Code, Replacements &Replaces) {
|
|||
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) {}
|
||||
|
|
|
@ -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;
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)))))))
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -151,6 +151,30 @@ TEST_F(ReplacementTest, ApplyAllFailsIfOneApplyFails) {
|
|||
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() {
|
||||
|
|
Loading…
Reference in New Issue