1 /** 2 Copyright: Copyright (c) 2017, Joakim Brännström. All rights reserved. 3 License: MPL-2 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 6 This Source Code Form is subject to the terms of the Mozilla Public License, 7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain 8 one at http://mozilla.org/MPL/2.0/. 9 */ 10 module autoformat.format_c_cpp; 11 12 import std.algorithm; 13 import std.array; 14 import std.exception; 15 import std.process; 16 import std.typecons : Flag; 17 18 import logger = std.experimental.logger; 19 20 import autoformat.types; 21 22 private immutable string[] astyleConf = import("astyle.conf").splitter("\n") 23 .filter!(a => a.length > 0).array ~ ["-Q"]; 24 25 private immutable string[] clangFormatConf = import("clang_format.conf").splitter( 26 "\n").filter!(a => a.length != 0).array; 27 28 // Thread local optimization that reduced the console spam when the program 29 // isn't installed. 30 private bool installed = true; 31 32 immutable clangToolEnvKey = "AUTOFORMAT_CLANG_TOOL"; 33 34 auto getClangFormatterTool() @safe nothrow { 35 try { 36 return environment.get(clangToolEnvKey, "clang-format"); 37 } catch (Exception e) { 38 } 39 return "astyle"; 40 } 41 42 auto runClangFormatter(AbsolutePath fname, Flag!"backup" backup, Flag!"dryRun" dry_run) nothrow { 43 string tool = getClangFormatterTool; 44 return runClangFormat(fname, backup, dry_run); 45 } 46 47 auto runClangFormat(AbsolutePath fname, Flag!"backup" backup, Flag!"dryRun" dry_run) nothrow { 48 import std.file : copy; 49 50 string[] opts = clangFormatConf.map!(a => a.idup).array; 51 52 if (!installed) { 53 return FormatterResult(Unchanged.init); 54 } 55 56 if (dry_run) 57 opts ~= "-output-replacements-xml"; 58 else 59 opts ~= "-i"; 60 61 auto arg = ["clang-format"] ~ opts ~ [cast(string) fname]; 62 63 auto rval = FormatterResult(FormatError.init); 64 65 try { 66 if (!dry_run && backup) { 67 copy(fname, fname.toString ~ ".orig"); 68 } 69 70 auto res = loggedExecute(arg); 71 72 if (dry_run && res.output.splitter("\n").count > 3) { 73 rval = FormatterResult(WouldChange.init); 74 } else { 75 rval = FormatterResult(FormattedOk.init); 76 } 77 } catch (ProcessException e) { 78 // clang-format isn't installed 79 rval = FormatterResult(FailedWithUserMsg(e.msg)); 80 installed = false; 81 } catch (Exception e) { 82 rval = FormatterResult(FailedWithUserMsg(e.msg)); 83 } 84 85 return rval; 86 } 87 88 auto loggedExecute(string[] arg) { 89 logger.trace(arg.join(" ")); 90 auto res = execute(arg); 91 logger.trace(res.output); 92 return res; 93 }