Skip to content

Swift format #8610

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions tools/driver/swift_format_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,6 @@ class SwiftFormatInvocation {
InputFilenames.push_back(A->getValue());
}

if (InputFilenames.empty()) {
Diags.diagnose(SourceLoc(), diag::error_mode_requires_an_input_file);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. Most Swift tools use an explicit - for this. Is accepting input on stdin better?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so. It simplifies the integration and matches the behaviour for clang-format.

return 1;
}

if (const Arg *A = ParsedArgs.getLastArg(OPT_o)) {
OutputFilename = A->getValue();
}
Expand Down Expand Up @@ -254,7 +249,7 @@ int swift_format_main(ArrayRef<const char *> Args, const char *Argv0,

DiagnosticEngine &Diags = Instance.getDiags();
if (Invocation.parseArgs(Args, Diags) != 0)
return 1;
return EXIT_FAILURE;

std::vector<std::string> InputFiles = Invocation.getInputFilenames();
unsigned NumInputFiles = InputFiles.size();
Expand All @@ -268,10 +263,11 @@ int swift_format_main(ArrayRef<const char *> Args, const char *Argv0,
// We don't support formatting file ranges for multiple files.
Instance.getDiags().diagnose(SourceLoc(),
diag::error_formatting_multiple_file_ranges);
return 1;
return EXIT_FAILURE;
}
for (unsigned i = 0; i < NumInputFiles; ++i)
Invocation.format(InputFiles[i], Diags);
}
return 0;

return EXIT_SUCCESS;
}
94 changes: 94 additions & 0 deletions utils/vim/swift-format.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# This file is a minimal swift-format vim-integration. To install:
# - Change 'binary' if swift-format is not on the path (see below).
# - Add to your .vimrc:
#
# map <C-I> :pyf <path-to-this-file>/swift-format.py<cr>
# imap <C-I> <c-o>:pyf <path-to-this-file>/swift-format.py<cr>
#
# The first line enables swift-format for NORMAL and VISUAL mode, the second
# line adds support for INSERT mode. Change "C-I" to another binding if you
# need swift-format on a different key (C-I stands for Ctrl+i).
#
# With this integration you can press the bound key and swift-format will
# format the current line in NORMAL and INSERT mode or the selected region in
# VISUAL mode. The line or region is extended to the next bigger syntactic
# entity.
#
# You can also pass in the variable "l:lines" to choose the range for
# formatting. This variable can either contain "<start line>:<end line> or
# "all" to format the full file. So, to format the full file, write a function
# like:
#
# :function FormatFile()
# : let l:lines="all"
# : pyf <path-to-this-file>/swift-format.py
# :endfunction
#
# It operates on the current, potentially unsaved buffer and does not create or
# save any files. To revert a formatting, just undo.

from __future__ import print_function

import difflib
import platform
import subprocess
import sys
import vim

binary = 'swift-format'
if vim.eval('exists("g:swift_format_path")') == "1":
binary = vim.eval('g:swift_format_path')


def get_buffer(encoding):
if platform.python_version_tuple()[0] == "3":
return vim.current.buffer
return [line.decode(encoding) for line in vim.current.buffer]


def main(argc, argv):
encoding = vim.eval("&encoding")
buf = get_buffer(encoding)

if vim.eval('exists("l:lines")') == '1':
lines = vim.eval('l:lines')
else:
lines = '%s:%s' % (vim.current.range.start + 1,
vim.current.range.end + 1)

cursor = int(vim.eval('line2byte(line(".")) + col(".")')) - 2
if cursor < 0:
print("Couldn't determine cursor position. Is your file empty?")
return

# avoid the cmd prompt on windows
SI = None
if sys.platform.startswith('win32'):
SI = subprocess.STARTUPINFO()
SI.dwFlags |= subprocess.STARTF_USESHOWWINDOW
SI.wShowWindow = subprocess.SW_HIDE

command = [binary]
if lines != 'all':
command.extend(['-line-range', lines])

p = subprocess.Popen(command,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
stdin=subprocess.PIPE, startupinfo=SI)
stdout, stderr = p.communicate(input='\n'.join(buf).encode(encoding))

if stderr:
print(stderr)

if not stdout:
print('No output from swift-format (crashed?).')
return

lines = stdout.decode(encoding).split('\n')
sequence = difflib.SequenceMatcher(None, buf, lines)
for op in reversed(sequence.get_opcodes()):
if op[0] is not 'equal':
vim.current.buffer[op[1]:op[2]] = lines[op[3]:op[4]]

if __name__ == '__main__':
main(len(sys.argv), sys.argv)