Android Build Tools
This commit is contained in:
@ -0,0 +1,65 @@
|
||||
# Please add "source /path/to/bash-autocomplete.sh" to your .bashrc to use this.
|
||||
|
||||
_clang_filedir()
|
||||
{
|
||||
# _filedir function provided by recent versions of bash-completion package is
|
||||
# better than "compgen -f" because the former honors spaces in pathnames while
|
||||
# the latter doesn't. So we use compgen only when _filedir is not provided.
|
||||
_filedir 2> /dev/null || COMPREPLY=( $( compgen -f ) )
|
||||
}
|
||||
|
||||
_clang()
|
||||
{
|
||||
local cur prev words cword arg flags w1 w2
|
||||
# If latest bash-completion is not supported just initialize COMPREPLY and
|
||||
# initialize variables by setting manually.
|
||||
_init_completion -n 2> /dev/null
|
||||
if [[ "$?" != 0 ]]; then
|
||||
COMPREPLY=()
|
||||
cword=$COMP_CWORD
|
||||
cur="${COMP_WORDS[$cword]}"
|
||||
fi
|
||||
|
||||
w1="${COMP_WORDS[$cword - 1]}"
|
||||
if [[ $cword > 1 ]]; then
|
||||
w2="${COMP_WORDS[$cword - 2]}"
|
||||
fi
|
||||
|
||||
# Pass all the current command-line flags to clang, so that clang can handle
|
||||
# these internally.
|
||||
# '=' is separated differently by bash, so we have to concat them without ','
|
||||
for i in `seq 1 $cword`; do
|
||||
if [[ $i == $cword || "${COMP_WORDS[$(($i+1))]}" == '=' ]]; then
|
||||
arg="$arg${COMP_WORDS[$i]}"
|
||||
else
|
||||
arg="$arg${COMP_WORDS[$i]},"
|
||||
fi
|
||||
done
|
||||
|
||||
# expand ~ to $HOME
|
||||
eval local path=${COMP_WORDS[0]}
|
||||
# Use $'\t' so that bash expands the \t for older versions of sed.
|
||||
flags=$( "$path" --autocomplete="$arg" 2>/dev/null | sed -e $'s/\t.*//' )
|
||||
# If clang is old that it does not support --autocomplete,
|
||||
# fall back to the filename completion.
|
||||
if [[ "$?" != 0 ]]; then
|
||||
_clang_filedir
|
||||
return
|
||||
fi
|
||||
|
||||
# When clang does not emit any possible autocompletion, or user pushed tab after " ",
|
||||
# just autocomplete files.
|
||||
if [[ "$flags" == "$(echo -e '\n')" ]]; then
|
||||
# If -foo=<tab> and there was no possible values, autocomplete files.
|
||||
[[ "$cur" == '=' || "$cur" == -*= ]] && cur=""
|
||||
_clang_filedir
|
||||
elif [[ "$cur" == '=' ]]; then
|
||||
COMPREPLY=( $( compgen -W "$flags" -- "") )
|
||||
else
|
||||
# Bash automatically appends a space after '=' by default.
|
||||
# Disable it so that it works nicely for options in the form of -foo=bar.
|
||||
[[ "${flags: -1}" == '=' ]] && compopt -o nospace 2> /dev/null
|
||||
COMPREPLY=( $( compgen -W "$flags" -- "$cur" ) )
|
||||
fi
|
||||
}
|
||||
complete -F _clang clang
|
||||
@ -0,0 +1,969 @@
|
||||
.dark-primary-color { background: #1976D2; }
|
||||
.default-primary-color { background: #2196F3; }
|
||||
.light-primary-color { background: #BBDEFB; }
|
||||
.text-primary-color { color: #FFFFFF; }
|
||||
.accent-color { background: #00BCD4; }
|
||||
.primary-text-color { color: #212121; }
|
||||
.secondary-text-color { color: #727272; }
|
||||
.divider-color { border-color: #B6B6B6; }
|
||||
|
||||
/* for layout */
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
*, *:before, *:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
header {
|
||||
flex: 0 0 50px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
header ol {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
header ol li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
header form {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
justify-content: flex-end;
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
header#header-search-sidebar {
|
||||
height: 50px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
footer {
|
||||
flex: 0 0 16px;
|
||||
text-align: center;
|
||||
padding: 16px 20px;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 20px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.sidebar-offcanvas-left {
|
||||
flex: 0 1 230px;
|
||||
overflow-y: scroll;
|
||||
padding: 20px 0 15px 30px;
|
||||
margin: 5px 20px 0 0;
|
||||
visibility: visible; /* shown by Javascript after scroll position restore */
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-button{ display: none; height: 13px; border-radius: 0px; background-color: #AAA; }
|
||||
::-webkit-scrollbar-button:hover{ background-color: #AAA; }
|
||||
::-webkit-scrollbar-thumb{ background-color: #CCC; }
|
||||
::-webkit-scrollbar-thumb:hover{ background-color: #CCC; }
|
||||
::-webkit-scrollbar{ width: 4px; }
|
||||
/* ::-webkit-overflow-scrolling: touch; */
|
||||
|
||||
.main-content::-webkit-scrollbar{ width: 8px; }
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
overflow-y: scroll;
|
||||
padding: 10px 20px 0 20px;
|
||||
visibility: visible; /* shown by Javascript after scroll position restore */
|
||||
}
|
||||
|
||||
.sidebar-offcanvas-right {
|
||||
flex: 0 1 12em;
|
||||
overflow-y: scroll;
|
||||
padding: 20px 15px 15px 15px;
|
||||
margin-top: 5px;
|
||||
margin-right: 20px;
|
||||
visibility: visible; /* shown by Javascript after scroll position restore */
|
||||
}
|
||||
/* end for layout */
|
||||
|
||||
body {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
overflow-x: hidden;
|
||||
font-family: Roboto, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.42857143;
|
||||
color: #111111;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/* some of this is to reset bootstrap */
|
||||
nav.navbar {
|
||||
background-color: inherit;
|
||||
min-height: 50px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.hidden-xs {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 769px) {
|
||||
.hidden-l {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
nav.navbar .row {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
nav .container {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: #eeeeee;
|
||||
box-shadow: 0 3px 5px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
header#project-title {
|
||||
background-color: #fff;
|
||||
font-size: 200%;
|
||||
padding-top: 0.25em;
|
||||
padding-bottom: 0.25em;
|
||||
/* padding: 0em; */
|
||||
}
|
||||
|
||||
header.header-fixed nav.navbar-fixed-top {
|
||||
box-shadow: 0 3px 5px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
header.container-fluid {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
header .masthead {
|
||||
padding-top: 64px;
|
||||
}
|
||||
|
||||
header .contents {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width:768px) {
|
||||
header .contents {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.body {
|
||||
margin-top: 90px;
|
||||
}
|
||||
|
||||
section {
|
||||
margin-bottom: 36px;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: Roboto, sans-serif;
|
||||
font-weight: 400;
|
||||
margin-top: 1.5em;
|
||||
color: #111111;
|
||||
}
|
||||
|
||||
h1.title {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 37px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.67em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 17px;
|
||||
min-height: 1.4em;
|
||||
}
|
||||
|
||||
.title-description .subtitle {
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 1em;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #0175C2;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #13B9FD;
|
||||
}
|
||||
|
||||
pre.prettyprint {
|
||||
font-family: 'Source Code Pro', Menlo, monospace;
|
||||
color: black;
|
||||
border-radius: 0;
|
||||
font-size: 15px;
|
||||
word-wrap: normal;
|
||||
line-height: 1.4;
|
||||
border: 0;
|
||||
margin: 16px 0 16px 0;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
pre code {
|
||||
white-space: pre;
|
||||
word-wrap: initial;
|
||||
font-size: 100%
|
||||
}
|
||||
|
||||
.fixed {
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
pre {
|
||||
border: 1px solid #ddd;
|
||||
background-color: #eee;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: 'Source Code Pro', Menlo, monospace;
|
||||
/* overriding bootstrap */
|
||||
color: inherit;
|
||||
padding: 0.2em 0.4em;
|
||||
font-size: 85%;
|
||||
background-color: rgba(27,31,35,0.05);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
@media(max-width: 768px) {
|
||||
nav .container {
|
||||
width: 100%
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 16px 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
ul.subnav li {
|
||||
font-size: 17px;
|
||||
}
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-weight: 400;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
header a,
|
||||
header p,
|
||||
header li {
|
||||
color: #111111;
|
||||
}
|
||||
|
||||
header a:hover {
|
||||
color: #0175C2;
|
||||
}
|
||||
|
||||
header h1 .kind {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
dd {
|
||||
color: #212121;
|
||||
margin-bottom: 1em;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
dd.callable, dd.constant, dd.property {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
dd p {
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* indents wrapped lines */
|
||||
section.summary dt {
|
||||
margin-left: 24px;
|
||||
text-indent: -24px;
|
||||
}
|
||||
|
||||
.dl-horizontal dd {
|
||||
margin-left: initial;
|
||||
}
|
||||
|
||||
dl.dl-horizontal dt {
|
||||
font-style: normal;
|
||||
text-align: left;
|
||||
color: #727272;
|
||||
margin-right: 20px;
|
||||
width: initial;
|
||||
}
|
||||
|
||||
dt .name {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
dl dt.callable .name {
|
||||
float: none;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.parameter {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.type-parameter {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.multi-line-signature .type-parameter .parameter {
|
||||
margin-left: 0px;
|
||||
display: unset;
|
||||
}
|
||||
|
||||
.signature {
|
||||
color: #727272;
|
||||
}
|
||||
|
||||
.signature a {
|
||||
/* 50% mix of default-primary-color and primary-text-color. */
|
||||
color: #4674a2;
|
||||
}
|
||||
|
||||
.optional {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.undocumented {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.is-const {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.deprecated {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.category.linked {
|
||||
font-weight: bold;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Colors for category based on categoryOrder in dartdoc_options.config. */
|
||||
.category.cp-0 {
|
||||
background-color: #54b7c4
|
||||
}
|
||||
|
||||
.category.cp-1 {
|
||||
background-color: #54c47f
|
||||
}
|
||||
|
||||
.category.cp-2 {
|
||||
background-color: #c4c254
|
||||
}
|
||||
|
||||
.category.cp-3 {
|
||||
background-color: #c49f54
|
||||
}
|
||||
|
||||
.category.cp-4 {
|
||||
background-color: #c45465
|
||||
}
|
||||
|
||||
.category.cp-5 {
|
||||
background-color: #c454c4
|
||||
}
|
||||
|
||||
.category a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.category {
|
||||
padding: 2px 4px;
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
background-color: #999;
|
||||
text-transform: uppercase;
|
||||
color: white;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
h1 .category {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.source-link {
|
||||
padding: 18px 4px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.source-link .material-icons {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.source-link {
|
||||
padding: 7px 2px;
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
#external-links {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
p.firstline {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
footer {
|
||||
color: #fff;
|
||||
background-color: #111111;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
footer p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
footer .no-break {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
footer .container,
|
||||
footer .container-fluid {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
footer a, footer a:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.markdown.desc {
|
||||
max-width: 700px;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
font-size: 24px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.markdown h2 {
|
||||
font-size: 20px;
|
||||
margin-top: 24px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.markdown h3 {
|
||||
font-size: 18px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.markdown h4 {
|
||||
font-size: 16px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.markdown li p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.gt-separated {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.gt-separated li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.gt-separated li:before {
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'><path fill='%23DDDDDD' d='M6.7,4L5.7,4.9L8.8,8l-3.1,3.1L6.7,12l4-4L6.7,4z'/></svg>");
|
||||
background-position: center;
|
||||
content: "\00a0";
|
||||
margin: 0 6px 0 4px;
|
||||
padding: 0 3px 0 0;
|
||||
}
|
||||
|
||||
.gt-separated.dark li:before {
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'><path fill='%23727272' d='M6.7,4L5.7,4.9L8.8,8l-3.1,3.1L6.7,12l4-4L6.7,4z'/></svg>");
|
||||
}
|
||||
|
||||
.gt-separated li:first-child:before {
|
||||
background-image: none;
|
||||
content: "";
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* The slug line under a declaration for things like "const", "read-only", etc. */
|
||||
.features {
|
||||
font-style: italic;
|
||||
color: #727272;
|
||||
}
|
||||
|
||||
.multi-line-signature {
|
||||
font-size: 17px;
|
||||
color: #727272;
|
||||
}
|
||||
|
||||
.multi-line-signature .parameter {
|
||||
margin-left: 24px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.breadcrumbs {
|
||||
padding: 0;
|
||||
margin: 8px 0 8px 0;
|
||||
white-space: nowrap;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
nav ol.breadcrumbs {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.breadcrumbs {
|
||||
margin: 0 0 24px 0;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.self-crumb {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.self-name {
|
||||
color: #555;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.annotation-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.comma-separated {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.comma-separated li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.comma-separated li:after {
|
||||
content: ", ";
|
||||
}
|
||||
|
||||
.comma-separated li:last-child:after {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.end-with-period li:last-child:after {
|
||||
content: ".";
|
||||
}
|
||||
|
||||
.container > section:first-child {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.constructor-modifier {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
section.multi-line-signature div.parameters {
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
/* subnav styles */
|
||||
|
||||
ul.subnav {
|
||||
overflow: auto;
|
||||
white-space: nowrap;
|
||||
padding-left: 0;
|
||||
min-height: 25px;
|
||||
}
|
||||
|
||||
ul.subnav::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
ul.subnav li {
|
||||
display: inline-block;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
ul.subnav li a {
|
||||
color: #111;
|
||||
}
|
||||
|
||||
ul.subnav li {
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
ul.subnav li:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
@media(max-width: 768px) {
|
||||
ul.subnav li {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* sidebar styles */
|
||||
|
||||
.sidebar ol {
|
||||
list-style: none;
|
||||
line-height: 22px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
padding: 0 0 15px 0;
|
||||
}
|
||||
|
||||
.sidebar h5 a,
|
||||
.sidebar h5 a:hover {
|
||||
color: #727272;
|
||||
}
|
||||
|
||||
.sidebar h5,
|
||||
.sidebar ol li {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
padding: 3px 0;
|
||||
}
|
||||
|
||||
.sidebar h5 {
|
||||
color: #727272;
|
||||
font-size: 18px;
|
||||
margin: 0 0 25px 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.sidebar ol li.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: normal;
|
||||
text-transform: uppercase;
|
||||
padding-top: 25px;
|
||||
}
|
||||
|
||||
.sidebar ol li.section-subtitle a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.sidebar ol li.section-subtitle {
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.sidebar ol li.section-subitem {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.sidebar ol li:first-child {
|
||||
padding-top: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#sidenav-left-toggle {
|
||||
display: none;
|
||||
vertical-align: text-bottom;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* left-nav disappears, and can transition in from the left */
|
||||
@media screen and (max-width:768px) {
|
||||
#sidenav-left-toggle {
|
||||
display: inline;
|
||||
background: no-repeat url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'><path fill='%23111' d='M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z'/></svg>");
|
||||
background-position: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: none;
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
#overlay-under-drawer.active {
|
||||
opacity: 0.4;
|
||||
height: 100%;
|
||||
z-index: 1999;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: black;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sidebar-offcanvas-left {
|
||||
left: -100%;
|
||||
position: fixed;
|
||||
-webkit-transition:all .25s ease-out;
|
||||
-o-transition:all .25s ease-out;
|
||||
transition:all .25s ease-out;
|
||||
z-index: 2000;
|
||||
top: 0;
|
||||
width: 280px; /* works all the way down to an iphone 4 */
|
||||
height: 90%;
|
||||
background-color: white;
|
||||
overflow-y: scroll; /* TODO: how to hide scroll bars? */
|
||||
padding: 10px;
|
||||
margin: 10px 10px;
|
||||
box-shadow: 5px 5px 5px 5px #444444;
|
||||
visibility: hidden; /* shown by Javascript after scroll position restore */
|
||||
}
|
||||
|
||||
ol#sidebar-nav {
|
||||
font-size: 18px;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.sidebar-offcanvas-left.active {
|
||||
left: 0; /* this animates our drawer into the page */
|
||||
}
|
||||
|
||||
.self-name {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-offcanvas-left h5 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.sidebar-offcanvas-left h5:last-of-type {
|
||||
border: 0;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
/* the right nav disappears out of view when the window shrinks */
|
||||
@media screen and (max-width: 992px) {
|
||||
.sidebar-offcanvas-right {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#overlay-under-drawer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* find-as-you-type search box */
|
||||
|
||||
/* override bootstrap defaults */
|
||||
.form-control {
|
||||
border-radius: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
form.search {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.typeahead,
|
||||
.tt-query,
|
||||
.tt-hint {
|
||||
width: 200px;
|
||||
height: 20px;
|
||||
padding: 2px 7px 1px 7px;
|
||||
line-height: 20px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.typeahead {
|
||||
background-color: #fff;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.tt-query {
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
|
||||
.tt-hint {
|
||||
color: #999
|
||||
}
|
||||
|
||||
.navbar-right .tt-menu {
|
||||
right:0;
|
||||
left: inherit !important;
|
||||
width: 422px;
|
||||
max-height: 250px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.tt-menu {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
padding: 8px 0;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
-webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
|
||||
-moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
|
||||
box-shadow: 0 5px 10px rgba(0,0,0,.2);
|
||||
}
|
||||
|
||||
.tt-suggestion {
|
||||
padding: 3px 20px;
|
||||
color: #212121;
|
||||
}
|
||||
|
||||
.tt-suggestion:hover {
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
background-color: #0097cf;
|
||||
}
|
||||
|
||||
.tt-suggestion:hover .search-from-lib {
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
.tt-suggestion.tt-cursor {
|
||||
color: #fff;
|
||||
background-color: #0097cf;
|
||||
}
|
||||
|
||||
.tt-suggestion.tt-cursor .search-from-lib {
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
.tt-suggestion p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.search-from-lib {
|
||||
font-style: italic;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
#search-box {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.search-body {
|
||||
border: 1px solid #7f7f7f;
|
||||
max-width: 400px;
|
||||
box-shadow: 3px 3px 5px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
section#setter {
|
||||
border-top: 1px solid #ddd;
|
||||
padding-top: 36px;
|
||||
}
|
||||
|
||||
li.inherited a {
|
||||
opacity: 0.65;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#instance-methods dt.inherited .name,
|
||||
#instance-properties dt.inherited .name,
|
||||
#operators dt.inherited .name {
|
||||
font-weight: 300;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#instance-methods dt.inherited .signature,
|
||||
#instance-properties dt.inherited .signature,
|
||||
#operators dt.inherited .signature {
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
@media print {
|
||||
.subnav, .sidebar {
|
||||
display:none;
|
||||
}
|
||||
|
||||
a[href]:after {
|
||||
content:"" !important;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
-- In this file, change "/path/to/" to the path where you installed clang-format
|
||||
-- and save it to ~/Library/Application Support/BBEdit/Scripts. You can then
|
||||
-- select the script from the Script menu and clang-format will format the
|
||||
-- selection. Note that you can rename the menu item by renaming the script, and
|
||||
-- can assign the menu item a keyboard shortcut in the BBEdit preferences, under
|
||||
-- Menus & Shortcuts.
|
||||
on urlToPOSIXPath(theURL)
|
||||
return do shell script "python -c \"import urllib, urlparse, sys; print urllib.unquote(urlparse.urlparse(sys.argv[1])[2])\" " & quoted form of theURL
|
||||
end urlToPOSIXPath
|
||||
|
||||
tell application "BBEdit"
|
||||
set selectionOffset to characterOffset of selection
|
||||
set selectionLength to length of selection
|
||||
set fileURL to URL of text document 1
|
||||
end tell
|
||||
|
||||
set filePath to urlToPOSIXPath(fileURL)
|
||||
set newContents to do shell script "/path/to/clang-format -offset=" & selectionOffset & " -length=" & selectionLength & " " & quoted form of filePath
|
||||
|
||||
tell application "BBEdit"
|
||||
-- "set contents of text document 1 to newContents" scrolls to the bottom while
|
||||
-- replacing a selection flashes a bit but doesn't affect the scroll position.
|
||||
set currentLength to length of contents of text document 1
|
||||
select characters 1 thru currentLength of text document 1
|
||||
set text of selection to newContents
|
||||
select characters selectionOffset thru (selectionOffset + selectionLength - 1) of text document 1
|
||||
end tell
|
||||
@ -0,0 +1,193 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# ===- clang-format-diff.py - ClangFormat Diff Reformatter ----*- python -*--===#
|
||||
#
|
||||
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
# See https://llvm.org/LICENSE.txt for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
#
|
||||
# ===------------------------------------------------------------------------===#
|
||||
|
||||
"""
|
||||
This script reads input from a unified diff and reformats all the changed
|
||||
lines. This is useful to reformat all the lines touched by a specific patch.
|
||||
Example usage for git/svn users:
|
||||
|
||||
git diff -U0 --no-color --relative HEAD^ | {clang_format_diff} -p1 -i
|
||||
svn diff --diff-cmd=diff -x-U0 | {clang_format_diff} -i
|
||||
|
||||
It should be noted that the filename contained in the diff is used unmodified
|
||||
to determine the source file to update. Users calling this script directly
|
||||
should be careful to ensure that the path in the diff is correct relative to the
|
||||
current working directory.
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
import argparse
|
||||
import difflib
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
if sys.version_info.major >= 3:
|
||||
from io import StringIO
|
||||
else:
|
||||
from io import BytesIO as StringIO
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description=__doc__.format(clang_format_diff="%(prog)s"),
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="apply edits to files instead of displaying a diff",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
metavar="NUM",
|
||||
default=0,
|
||||
help="strip the smallest prefix containing P slashes",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-regex",
|
||||
metavar="PATTERN",
|
||||
default=None,
|
||||
help="custom pattern selecting file paths to reformat "
|
||||
"(case sensitive, overrides -iregex)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-iregex",
|
||||
metavar="PATTERN",
|
||||
default=r".*\.(?:cpp|cc|c\+\+|cxx|cppm|ccm|cxxm|c\+\+m|c|cl|h|hh|hpp"
|
||||
r"|hxx|m|mm|inc|js|ts|proto|protodevel|java|cs|json|s?vh?)",
|
||||
help="custom pattern selecting file paths to reformat "
|
||||
"(case insensitive, overridden by -regex)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-sort-includes",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="let clang-format sort include blocks",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--verbose",
|
||||
action="store_true",
|
||||
help="be more verbose, ineffective without -i",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-style",
|
||||
help="formatting style to apply (LLVM, GNU, Google, Chromium, "
|
||||
"Microsoft, Mozilla, WebKit)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-fallback-style",
|
||||
help="The name of the predefined style used as a"
|
||||
"fallback in case clang-format is invoked with"
|
||||
"-style=file, but can not find the .clang-format"
|
||||
"file to use.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-binary",
|
||||
default="clang-format",
|
||||
help="location of binary to use for clang-format",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Extract changed lines for each file.
|
||||
filename = None
|
||||
lines_by_file = {}
|
||||
for line in sys.stdin:
|
||||
match = re.search(r"^\+\+\+\ (.*?/){%s}(\S*)" % args.p, line)
|
||||
if match:
|
||||
filename = match.group(2)
|
||||
if filename is None:
|
||||
continue
|
||||
|
||||
if args.regex is not None:
|
||||
if not re.match("^%s$" % args.regex, filename):
|
||||
continue
|
||||
else:
|
||||
if not re.match("^%s$" % args.iregex, filename, re.IGNORECASE):
|
||||
continue
|
||||
|
||||
match = re.search(r"^@@.*\+(\d+)(?:,(\d+))?", line)
|
||||
if match:
|
||||
start_line = int(match.group(1))
|
||||
line_count = 1
|
||||
if match.group(2):
|
||||
line_count = int(match.group(2))
|
||||
# The input is something like
|
||||
#
|
||||
# @@ -1, +0,0 @@
|
||||
#
|
||||
# which means no lines were added.
|
||||
if line_count == 0:
|
||||
continue
|
||||
# Also format lines range if line_count is 0 in case of deleting
|
||||
# surrounding statements.
|
||||
end_line = start_line
|
||||
if line_count != 0:
|
||||
end_line += line_count - 1
|
||||
lines_by_file.setdefault(filename, []).extend(
|
||||
["-lines", str(start_line) + ":" + str(end_line)]
|
||||
)
|
||||
|
||||
# Reformat files containing changes in place.
|
||||
for filename, lines in lines_by_file.items():
|
||||
if args.i and args.verbose:
|
||||
print("Formatting {}".format(filename))
|
||||
command = [args.binary, filename]
|
||||
if args.i:
|
||||
command.append("-i")
|
||||
if args.sort_includes:
|
||||
command.append("-sort-includes")
|
||||
command.extend(lines)
|
||||
if args.style:
|
||||
command.extend(["-style", args.style])
|
||||
if args.fallback_style:
|
||||
command.extend(["-fallback-style", args.fallback_style])
|
||||
|
||||
try:
|
||||
p = subprocess.Popen(
|
||||
command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=None,
|
||||
stdin=subprocess.PIPE,
|
||||
universal_newlines=True,
|
||||
)
|
||||
except OSError as e:
|
||||
# Give the user more context when clang-format isn't
|
||||
# found/isn't executable, etc.
|
||||
raise RuntimeError(
|
||||
'Failed to run "%s" - %s"' % (" ".join(command), e.strerror)
|
||||
)
|
||||
|
||||
stdout, stderr = p.communicate()
|
||||
if p.returncode != 0:
|
||||
sys.exit(p.returncode)
|
||||
|
||||
if not args.i:
|
||||
with open(filename) as f:
|
||||
code = f.readlines()
|
||||
formatted_code = StringIO(stdout).readlines()
|
||||
diff = difflib.unified_diff(
|
||||
code,
|
||||
formatted_code,
|
||||
filename,
|
||||
filename,
|
||||
"(before formatting)",
|
||||
"(after formatting)",
|
||||
)
|
||||
diff_string = "".join(diff)
|
||||
if len(diff_string) > 0:
|
||||
sys.stdout.write(diff_string)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -0,0 +1,73 @@
|
||||
# This file is a minimal clang-format sublime-integration. To install:
|
||||
# - Change 'binary' if clang-format is not on the path (see below).
|
||||
# - Put this file into your sublime Packages directory, e.g. on Linux:
|
||||
# ~/.config/sublime-text-2/Packages/User/clang-format-sublime.py
|
||||
# - Add a key binding:
|
||||
# { "keys": ["ctrl+shift+c"], "command": "clang_format" },
|
||||
#
|
||||
# With this integration you can press the bound key and clang-format will
|
||||
# format the current lines and selections for all cursor positions. The lines
|
||||
# or regions are extended to the next bigger syntactic entities.
|
||||
#
|
||||
# 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 absolute_import, division, print_function
|
||||
import sublime
|
||||
import sublime_plugin
|
||||
import subprocess
|
||||
|
||||
# Change this to the full path if clang-format is not on the path.
|
||||
binary = "clang-format"
|
||||
|
||||
# Change this to format according to other formatting styles. See the output of
|
||||
# 'clang-format --help' for a list of supported styles. The default looks for
|
||||
# a '.clang-format' or '_clang-format' file to indicate the style that should be
|
||||
# used.
|
||||
style = None
|
||||
|
||||
|
||||
class ClangFormatCommand(sublime_plugin.TextCommand):
|
||||
def run(self, edit):
|
||||
encoding = self.view.encoding()
|
||||
if encoding == "Undefined":
|
||||
encoding = "utf-8"
|
||||
regions = []
|
||||
command = [binary]
|
||||
if style:
|
||||
command.extend(["-style", style])
|
||||
for region in self.view.sel():
|
||||
regions.append(region)
|
||||
region_offset = min(region.a, region.b)
|
||||
region_length = abs(region.b - region.a)
|
||||
command.extend(
|
||||
[
|
||||
"-offset",
|
||||
str(region_offset),
|
||||
"-length",
|
||||
str(region_length),
|
||||
"-assume-filename",
|
||||
str(self.view.file_name()),
|
||||
]
|
||||
)
|
||||
old_viewport_position = self.view.viewport_position()
|
||||
buf = self.view.substr(sublime.Region(0, self.view.size()))
|
||||
p = subprocess.Popen(
|
||||
command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
stdin=subprocess.PIPE,
|
||||
)
|
||||
output, error = p.communicate(buf.encode(encoding))
|
||||
if error:
|
||||
print(error)
|
||||
self.view.replace(
|
||||
edit, sublime.Region(0, self.view.size()), output.decode(encoding)
|
||||
)
|
||||
self.view.sel().clear()
|
||||
for region in regions:
|
||||
self.view.sel().add(region)
|
||||
# FIXME: Without the 10ms delay, the viewport sometimes jumps.
|
||||
sublime.set_timeout(
|
||||
lambda: self.view.set_viewport_position(old_viewport_position, False), 10
|
||||
)
|
||||
@ -0,0 +1,220 @@
|
||||
;;; clang-format.el --- Format code using clang-format -*- lexical-binding: t; -*-
|
||||
|
||||
;; Keywords: tools, c
|
||||
;; Package-Requires: ((cl-lib "0.3"))
|
||||
;; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This package allows to filter code through clang-format to fix its formatting.
|
||||
;; clang-format is a tool that formats C/C++/Obj-C code according to a set of
|
||||
;; style options, see <http://clang.llvm.org/docs/ClangFormatStyleOptions.html>.
|
||||
;; Note that clang-format 3.4 or newer is required.
|
||||
|
||||
;; clang-format.el is available via MELPA and can be installed via
|
||||
;;
|
||||
;; M-x package-install clang-format
|
||||
;;
|
||||
;; when ("melpa" . "http://melpa.org/packages/") is included in
|
||||
;; `package-archives'. Alternatively, ensure the directory of this
|
||||
;; file is in your `load-path' and add
|
||||
;;
|
||||
;; (require 'clang-format)
|
||||
;;
|
||||
;; to your .emacs configuration.
|
||||
|
||||
;; You may also want to bind `clang-format-region' to a key:
|
||||
;;
|
||||
;; (global-set-key [C-M-tab] 'clang-format-region)
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'xml)
|
||||
|
||||
(defgroup clang-format nil
|
||||
"Format code using clang-format."
|
||||
:group 'tools)
|
||||
|
||||
(defcustom clang-format-executable
|
||||
(or (executable-find "clang-format")
|
||||
"clang-format")
|
||||
"Location of the clang-format executable.
|
||||
|
||||
A string containing the name or the full path of the executable."
|
||||
:group 'clang-format
|
||||
:type '(file :must-match t)
|
||||
:risky t)
|
||||
|
||||
(defcustom clang-format-style nil
|
||||
"Style argument to pass to clang-format.
|
||||
|
||||
By default clang-format will load the style configuration from
|
||||
a file named .clang-format located in one of the parent directories
|
||||
of the buffer."
|
||||
:group 'clang-format
|
||||
:type '(choice (string) (const nil))
|
||||
:safe #'stringp)
|
||||
(make-variable-buffer-local 'clang-format-style)
|
||||
|
||||
(defcustom clang-format-fallback-style "none"
|
||||
"Fallback style to pass to clang-format.
|
||||
|
||||
This style will be used if clang-format-style is set to \"file\"
|
||||
and no .clang-format is found in the directory of the buffer or
|
||||
one of parent directories. Set to \"none\" to disable formatting
|
||||
in such buffers."
|
||||
:group 'clang-format
|
||||
:type 'string
|
||||
:safe #'stringp)
|
||||
(make-variable-buffer-local 'clang-format-fallback-style)
|
||||
|
||||
(defun clang-format--extract (xml-node)
|
||||
"Extract replacements and cursor information from XML-NODE."
|
||||
(unless (and (listp xml-node) (eq (xml-node-name xml-node) 'replacements))
|
||||
(error "Expected <replacements> node"))
|
||||
(let ((nodes (xml-node-children xml-node))
|
||||
(incomplete-format (xml-get-attribute xml-node 'incomplete_format))
|
||||
replacements
|
||||
cursor)
|
||||
(dolist (node nodes)
|
||||
(when (listp node)
|
||||
(let* ((children (xml-node-children node))
|
||||
(text (car children)))
|
||||
(cl-case (xml-node-name node)
|
||||
(replacement
|
||||
(let* ((offset (xml-get-attribute-or-nil node 'offset))
|
||||
(length (xml-get-attribute-or-nil node 'length)))
|
||||
(when (or (null offset) (null length))
|
||||
(error "<replacement> node does not have offset and length attributes"))
|
||||
(when (cdr children)
|
||||
(error "More than one child node in <replacement> node"))
|
||||
|
||||
(setq offset (string-to-number offset))
|
||||
(setq length (string-to-number length))
|
||||
(push (list offset length text) replacements)))
|
||||
(cursor
|
||||
(setq cursor (string-to-number text)))))))
|
||||
|
||||
;; Sort by decreasing offset, length.
|
||||
(setq replacements (sort (delq nil replacements)
|
||||
(lambda (a b)
|
||||
(or (> (car a) (car b))
|
||||
(and (= (car a) (car b))
|
||||
(> (cadr a) (cadr b)))))))
|
||||
|
||||
(list replacements cursor (string= incomplete-format "true"))))
|
||||
|
||||
(defun clang-format--replace (offset length &optional text)
|
||||
"Replace the region defined by OFFSET and LENGTH with TEXT.
|
||||
OFFSET and LENGTH are measured in bytes, not characters. OFFSET
|
||||
is a zero-based file offset, assuming ‘utf-8-unix’ coding."
|
||||
(let ((start (clang-format--filepos-to-bufferpos offset 'exact 'utf-8-unix))
|
||||
(end (clang-format--filepos-to-bufferpos (+ offset length) 'exact
|
||||
'utf-8-unix)))
|
||||
(goto-char start)
|
||||
(delete-region start end)
|
||||
(when text
|
||||
(insert text))))
|
||||
|
||||
;; ‘bufferpos-to-filepos’ and ‘filepos-to-bufferpos’ are new in Emacs 25.1.
|
||||
;; Provide fallbacks for older versions.
|
||||
(defalias 'clang-format--bufferpos-to-filepos
|
||||
(if (fboundp 'bufferpos-to-filepos)
|
||||
'bufferpos-to-filepos
|
||||
(lambda (position &optional _quality _coding-system)
|
||||
(1- (position-bytes position)))))
|
||||
|
||||
(defalias 'clang-format--filepos-to-bufferpos
|
||||
(if (fboundp 'filepos-to-bufferpos)
|
||||
'filepos-to-bufferpos
|
||||
(lambda (byte &optional _quality _coding-system)
|
||||
(byte-to-position (1+ byte)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun clang-format-region (start end &optional style assume-file-name)
|
||||
"Use clang-format to format the code between START and END according to STYLE.
|
||||
If called interactively uses the region or the current statement if there is no
|
||||
no active region. If no STYLE is given uses `clang-format-style'. Use
|
||||
ASSUME-FILE-NAME to locate a style config file, if no ASSUME-FILE-NAME is given
|
||||
uses the function `buffer-file-name'."
|
||||
(interactive
|
||||
(if (use-region-p)
|
||||
(list (region-beginning) (region-end))
|
||||
(list (point) (point))))
|
||||
|
||||
(unless style
|
||||
(setq style clang-format-style))
|
||||
|
||||
(unless assume-file-name
|
||||
(setq assume-file-name (buffer-file-name (buffer-base-buffer))))
|
||||
|
||||
(let ((file-start (clang-format--bufferpos-to-filepos start 'approximate
|
||||
'utf-8-unix))
|
||||
(file-end (clang-format--bufferpos-to-filepos end 'approximate
|
||||
'utf-8-unix))
|
||||
(cursor (clang-format--bufferpos-to-filepos (point) 'exact 'utf-8-unix))
|
||||
(temp-buffer (generate-new-buffer " *clang-format-temp*"))
|
||||
(temp-file (make-temp-file "clang-format"))
|
||||
;; Output is XML, which is always UTF-8. Input encoding should match
|
||||
;; the encoding used to convert between buffer and file positions,
|
||||
;; otherwise the offsets calculated above are off. For simplicity, we
|
||||
;; always use ‘utf-8-unix’ and ignore the buffer coding system.
|
||||
(default-process-coding-system '(utf-8-unix . utf-8-unix)))
|
||||
(unwind-protect
|
||||
(let ((status (apply #'call-process-region
|
||||
nil nil clang-format-executable
|
||||
nil `(,temp-buffer ,temp-file) nil
|
||||
`("-output-replacements-xml"
|
||||
;; Guard against a nil assume-file-name.
|
||||
;; If the clang-format option -assume-filename
|
||||
;; is given a blank string it will crash as per
|
||||
;; the following bug report
|
||||
;; https://bugs.llvm.org/show_bug.cgi?id=34667
|
||||
,@(and assume-file-name
|
||||
(list "-assume-filename" assume-file-name))
|
||||
,@(and style (list "-style" style))
|
||||
"-fallback-style" ,clang-format-fallback-style
|
||||
"-offset" ,(number-to-string file-start)
|
||||
"-length" ,(number-to-string (- file-end file-start))
|
||||
"-cursor" ,(number-to-string cursor))))
|
||||
(stderr (with-temp-buffer
|
||||
(unless (zerop (cadr (insert-file-contents temp-file)))
|
||||
(insert ": "))
|
||||
(buffer-substring-no-properties
|
||||
(point-min) (line-end-position)))))
|
||||
(cond
|
||||
((stringp status)
|
||||
(error "(clang-format killed by signal %s%s)" status stderr))
|
||||
((not (zerop status))
|
||||
(error "(clang-format failed with code %d%s)" status stderr)))
|
||||
|
||||
(cl-destructuring-bind (replacements cursor incomplete-format)
|
||||
(with-current-buffer temp-buffer
|
||||
(clang-format--extract (car (xml-parse-region))))
|
||||
(save-excursion
|
||||
(dolist (rpl replacements)
|
||||
(apply #'clang-format--replace rpl)))
|
||||
(when cursor
|
||||
(goto-char (clang-format--filepos-to-bufferpos cursor 'exact
|
||||
'utf-8-unix)))
|
||||
(if incomplete-format
|
||||
(message "(clang-format: incomplete (syntax errors)%s)" stderr)
|
||||
(message "(clang-format: success%s)" stderr))))
|
||||
(delete-file temp-file)
|
||||
(when (buffer-name temp-buffer) (kill-buffer temp-buffer)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun clang-format-buffer (&optional style assume-file-name)
|
||||
"Use clang-format to format the current buffer according to STYLE.
|
||||
If no STYLE is given uses `clang-format-style'. Use ASSUME-FILE-NAME
|
||||
to locate a style config file. If no ASSUME-FILE-NAME is given uses
|
||||
the function `buffer-file-name'."
|
||||
(interactive)
|
||||
(clang-format-region (point-min) (point-max) style assume-file-name))
|
||||
|
||||
;;;###autoload
|
||||
(defalias 'clang-format 'clang-format-region)
|
||||
|
||||
(provide 'clang-format)
|
||||
;;; clang-format.el ends here
|
||||
@ -0,0 +1,168 @@
|
||||
# This file is a minimal clang-format vim-integration. To install:
|
||||
# - Change 'binary' if clang-format is not on the path (see below).
|
||||
# - Add to your .vimrc:
|
||||
#
|
||||
# if has('python')
|
||||
# map <C-I> :pyf <path-to-this-file>/clang-format.py<cr>
|
||||
# imap <C-I> <c-o>:pyf <path-to-this-file>/clang-format.py<cr>
|
||||
# elseif has('python3')
|
||||
# map <C-I> :py3f <path-to-this-file>/clang-format.py<cr>
|
||||
# imap <C-I> <c-o>:py3f <path-to-this-file>/clang-format.py<cr>
|
||||
# endif
|
||||
#
|
||||
# The if-elseif-endif conditional should pick either the python3 or python2
|
||||
# integration depending on your vim setup.
|
||||
#
|
||||
# The first mapping enables clang-format for NORMAL and VISUAL mode, the second
|
||||
# mapping adds support for INSERT mode. Change "C-I" to another binding if you
|
||||
# need clang-format on a different key (C-I stands for Ctrl+i).
|
||||
#
|
||||
# With this integration you can press the bound key and clang-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"
|
||||
# : if has('python')
|
||||
# : pyf <path-to-this-file>/clang-format.py
|
||||
# : elseif has('python3')
|
||||
# : py3f <path-to-this-file>/clang-format.py
|
||||
# : endif
|
||||
# :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 absolute_import, division, print_function
|
||||
|
||||
import difflib
|
||||
import json
|
||||
import os.path
|
||||
import platform
|
||||
import subprocess
|
||||
import sys
|
||||
import vim
|
||||
|
||||
# set g:clang_format_path to the path to clang-format if it is not on the path
|
||||
# Change this to the full path if clang-format is not on the path.
|
||||
binary = "clang-format"
|
||||
if vim.eval('exists("g:clang_format_path")') == "1":
|
||||
binary = vim.eval("g:clang_format_path")
|
||||
|
||||
# Change this to format according to other formatting styles. See the output of
|
||||
# 'clang-format --help' for a list of supported styles. The default looks for
|
||||
# a '.clang-format' or '_clang-format' file to indicate the style that should be
|
||||
# used.
|
||||
style = None
|
||||
fallback_style = None
|
||||
if vim.eval('exists("g:clang_format_fallback_style")') == "1":
|
||||
fallback_style = vim.eval("g:clang_format_fallback_style")
|
||||
|
||||
|
||||
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():
|
||||
# Get the current text.
|
||||
encoding = vim.eval("&encoding")
|
||||
buf = get_buffer(encoding)
|
||||
# Join the buffer into a single string with a terminating newline
|
||||
text = ("\n".join(buf) + "\n").encode(encoding)
|
||||
|
||||
# Determine range to format.
|
||||
if vim.eval('exists("l:lines")') == "1":
|
||||
lines = ["-lines", vim.eval("l:lines")]
|
||||
elif vim.eval('exists("l:formatdiff")') == "1" and os.path.exists(
|
||||
vim.current.buffer.name
|
||||
):
|
||||
with open(vim.current.buffer.name, "r") as f:
|
||||
ondisk = f.read().splitlines()
|
||||
sequence = difflib.SequenceMatcher(None, ondisk, vim.current.buffer)
|
||||
lines = []
|
||||
for op in reversed(sequence.get_opcodes()):
|
||||
if op[0] not in ["equal", "delete"]:
|
||||
lines += ["-lines", "%s:%s" % (op[3] + 1, op[4])]
|
||||
if lines == []:
|
||||
return
|
||||
else:
|
||||
lines = [
|
||||
"-lines",
|
||||
"%s:%s" % (vim.current.range.start + 1, vim.current.range.end + 1),
|
||||
]
|
||||
|
||||
# Convert cursor (line, col) to bytes.
|
||||
# Don't use line2byte: https://github.com/vim/vim/issues/5930
|
||||
_, cursor_line, cursor_col, _ = vim.eval('getpos(".")') # 1-based
|
||||
cursor_byte = 0
|
||||
for line in text.split(b"\n")[: int(cursor_line) - 1]:
|
||||
cursor_byte += len(line) + 1
|
||||
cursor_byte += int(cursor_col) - 1
|
||||
if cursor_byte < 0:
|
||||
print("Couldn't determine cursor position. Is your file empty?")
|
||||
return
|
||||
|
||||
# Avoid flashing an ugly, ugly cmd prompt on Windows when invoking clang-format.
|
||||
startupinfo = None
|
||||
if sys.platform.startswith("win32"):
|
||||
startupinfo = subprocess.STARTUPINFO()
|
||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
startupinfo.wShowWindow = subprocess.SW_HIDE
|
||||
|
||||
# Call formatter.
|
||||
command = [binary, "-cursor", str(cursor_byte)]
|
||||
if lines != ["-lines", "all"]:
|
||||
command += lines
|
||||
if style:
|
||||
command.extend(["-style", style])
|
||||
if fallback_style:
|
||||
command.extend(["-fallback-style", fallback_style])
|
||||
if vim.current.buffer.name:
|
||||
command.extend(["-assume-filename", vim.current.buffer.name])
|
||||
p = subprocess.Popen(
|
||||
command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
stdin=subprocess.PIPE,
|
||||
startupinfo=startupinfo,
|
||||
)
|
||||
stdout, stderr = p.communicate(input=text)
|
||||
|
||||
# If successful, replace buffer contents.
|
||||
if stderr:
|
||||
print(stderr)
|
||||
|
||||
if not stdout:
|
||||
print(
|
||||
"No output from clang-format (crashed?).\n"
|
||||
"Please report to bugs.llvm.org."
|
||||
)
|
||||
else:
|
||||
header, content = stdout.split(b"\n", 1)
|
||||
header = json.loads(header.decode("utf-8"))
|
||||
# Strip off the trailing newline (added above).
|
||||
# This maintains trailing empty lines present in the buffer if
|
||||
# the -lines specification requests them to remain unchanged.
|
||||
lines = content.decode(encoding).split("\n")[:-1]
|
||||
sequence = difflib.SequenceMatcher(None, buf, lines)
|
||||
for op in reversed(sequence.get_opcodes()):
|
||||
if op[0] != "equal":
|
||||
vim.current.buffer[op[1] : op[2]] = lines[op[3] : op[4]]
|
||||
if header.get("IncompleteFormat"):
|
||||
print("clang-format: incomplete (syntax errors)")
|
||||
# Convert cursor bytes to (line, col)
|
||||
# Don't use goto: https://github.com/vim/vim/issues/5930
|
||||
cursor_byte = int(header["Cursor"])
|
||||
prefix = content[0:cursor_byte]
|
||||
cursor_line = 1 + prefix.count(b"\n")
|
||||
cursor_column = 1 + len(prefix.rsplit(b"\n", 1)[-1])
|
||||
vim.command("call cursor(%d, %d)" % (cursor_line, cursor_column))
|
||||
|
||||
|
||||
main()
|
||||
@ -0,0 +1,460 @@
|
||||
;;; clang-include-fixer.el --- Emacs integration of the clang include fixer -*- lexical-binding: t; -*-
|
||||
|
||||
;; Keywords: tools, c
|
||||
;; Package-Requires: ((cl-lib "0.5") (json "1.2") (let-alist "1.0.4"))
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This package allows Emacs users to invoke the 'clang-include-fixer' within
|
||||
;; Emacs. 'clang-include-fixer' provides an automated way of adding #include
|
||||
;; directives for missing symbols in one translation unit, see
|
||||
;; <http://clang.llvm.org/extra/clang-include-fixer.html>.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'json)
|
||||
(require 'let-alist)
|
||||
|
||||
(defgroup clang-include-fixer nil
|
||||
"Clang-based include fixer."
|
||||
:group 'tools)
|
||||
|
||||
(defvar clang-include-fixer-add-include-hook nil
|
||||
"A hook that will be called for every added include.
|
||||
The first argument is the filename of the include, the second argument is
|
||||
non-nil if the include is a system-header.")
|
||||
|
||||
(defcustom clang-include-fixer-executable
|
||||
"clang-include-fixer"
|
||||
"Location of the clang-include-fixer executable.
|
||||
|
||||
A string containing the name or the full path of the executable."
|
||||
:group 'clang-include-fixer
|
||||
:type '(file :must-match t)
|
||||
:risky t)
|
||||
|
||||
(defcustom clang-include-fixer-input-format
|
||||
'yaml
|
||||
"Input format for clang-include-fixer.
|
||||
This string is passed as -db argument to
|
||||
`clang-include-fixer-executable'."
|
||||
:group 'clang-include-fixer
|
||||
:type '(radio
|
||||
(const :tag "Hard-coded mapping" :fixed)
|
||||
(const :tag "YAML" yaml)
|
||||
(symbol :tag "Other"))
|
||||
:risky t)
|
||||
|
||||
(defcustom clang-include-fixer-init-string
|
||||
""
|
||||
"Database initialization string for clang-include-fixer.
|
||||
This string is passed as -input argument to
|
||||
`clang-include-fixer-executable'."
|
||||
:group 'clang-include-fixer
|
||||
:type 'string
|
||||
:risky t)
|
||||
|
||||
(defface clang-include-fixer-highlight '((t :background "green"))
|
||||
"Used for highlighting the symbol for which a header file is being added.")
|
||||
|
||||
;;;###autoload
|
||||
(defun clang-include-fixer ()
|
||||
"Invoke the Include Fixer to insert missing C++ headers."
|
||||
(interactive)
|
||||
(message (concat "Calling the include fixer. "
|
||||
"This might take some seconds. Please wait."))
|
||||
(clang-include-fixer--start #'clang-include-fixer--add-header
|
||||
"-output-headers"))
|
||||
|
||||
;;;###autoload
|
||||
(defun clang-include-fixer-at-point ()
|
||||
"Invoke the Clang include fixer for the symbol at point."
|
||||
(interactive)
|
||||
(let ((symbol (clang-include-fixer--symbol-at-point)))
|
||||
(unless symbol
|
||||
(user-error "No symbol at current location"))
|
||||
(clang-include-fixer-from-symbol symbol)))
|
||||
|
||||
;;;###autoload
|
||||
(defun clang-include-fixer-from-symbol (symbol)
|
||||
"Invoke the Clang include fixer for the SYMBOL.
|
||||
When called interactively, prompts the user for a symbol."
|
||||
(interactive
|
||||
(list (read-string "Symbol: " (clang-include-fixer--symbol-at-point))))
|
||||
(clang-include-fixer--start #'clang-include-fixer--add-header
|
||||
(format "-query-symbol=%s" symbol)))
|
||||
|
||||
(defun clang-include-fixer--start (callback &rest args)
|
||||
"Asynchronously start clang-include-fixer with parameters ARGS.
|
||||
The current file name is passed after ARGS as last argument. If
|
||||
the call was successful the returned result is stored in a
|
||||
temporary buffer, and CALLBACK is called with the temporary
|
||||
buffer as only argument."
|
||||
(unless buffer-file-name
|
||||
(user-error "clang-include-fixer works only in buffers that visit a file"))
|
||||
(let ((process (if (and (fboundp 'make-process)
|
||||
;; ‘make-process’ doesn’t support remote files
|
||||
;; (https://debbugs.gnu.org/cgi/bugreport.cgi?bug=28691).
|
||||
(not (find-file-name-handler default-directory
|
||||
'start-file-process)))
|
||||
;; Prefer using ‘make-process’ if possible, because
|
||||
;; ‘start-process’ doesn’t allow us to separate the
|
||||
;; standard error from the output.
|
||||
(clang-include-fixer--make-process callback args)
|
||||
(clang-include-fixer--start-process callback args))))
|
||||
(save-restriction
|
||||
(widen)
|
||||
(process-send-region process (point-min) (point-max)))
|
||||
(process-send-eof process))
|
||||
nil)
|
||||
|
||||
(defun clang-include-fixer--make-process (callback args)
|
||||
"Start a new clang-include-fixer process using `make-process'.
|
||||
CALLBACK is called after the process finishes successfully; it is
|
||||
called with a single argument, the buffer where standard output
|
||||
has been inserted. ARGS is a list of additional command line
|
||||
arguments. Return the new process object."
|
||||
(let ((stdin (current-buffer))
|
||||
(stdout (generate-new-buffer "*clang-include-fixer output*"))
|
||||
(stderr (generate-new-buffer "*clang-include-fixer errors*")))
|
||||
(make-process :name "clang-include-fixer"
|
||||
:buffer stdout
|
||||
:command (clang-include-fixer--command args)
|
||||
:coding 'utf-8-unix
|
||||
:noquery t
|
||||
:connection-type 'pipe
|
||||
:sentinel (clang-include-fixer--sentinel stdin stdout stderr
|
||||
callback)
|
||||
:stderr stderr)))
|
||||
|
||||
(defun clang-include-fixer--start-process (callback args)
|
||||
"Start a new clang-include-fixer process using `start-file-process'.
|
||||
CALLBACK is called after the process finishes successfully; it is
|
||||
called with a single argument, the buffer where standard output
|
||||
has been inserted. ARGS is a list of additional command line
|
||||
arguments. Return the new process object."
|
||||
(let* ((stdin (current-buffer))
|
||||
(stdout (generate-new-buffer "*clang-include-fixer output*"))
|
||||
(process-connection-type nil)
|
||||
(process (apply #'start-file-process "clang-include-fixer" stdout
|
||||
(clang-include-fixer--command args))))
|
||||
(set-process-coding-system process 'utf-8-unix 'utf-8-unix)
|
||||
(set-process-query-on-exit-flag process nil)
|
||||
(set-process-sentinel process
|
||||
(clang-include-fixer--sentinel stdin stdout nil
|
||||
callback))
|
||||
process))
|
||||
|
||||
(defun clang-include-fixer--command (args)
|
||||
"Return the clang-include-fixer command line.
|
||||
Returns a list; the first element is the binary to
|
||||
execute (`clang-include-fixer-executable'), and the remaining
|
||||
elements are the command line arguments. Adds proper arguments
|
||||
for `clang-include-fixer-input-format' and
|
||||
`clang-include-fixer-init-string'. Appends the current buffer's
|
||||
file name; prepends ARGS directly in front of it."
|
||||
(cl-check-type args list)
|
||||
`(,clang-include-fixer-executable
|
||||
,(format "-db=%s" clang-include-fixer-input-format)
|
||||
,(format "-input=%s" clang-include-fixer-init-string)
|
||||
"-stdin"
|
||||
,@args
|
||||
,(clang-include-fixer--file-local-name buffer-file-name)))
|
||||
|
||||
(defun clang-include-fixer--sentinel (stdin stdout stderr callback)
|
||||
"Return a process sentinel for clang-include-fixer processes.
|
||||
STDIN, STDOUT, and STDERR are buffers for the standard streams;
|
||||
only STDERR may be nil. CALLBACK is called in the case of
|
||||
success; it is called with a single argument, STDOUT. On
|
||||
failure, a buffer containing the error output is displayed."
|
||||
(cl-check-type stdin buffer)
|
||||
(cl-check-type stdout buffer)
|
||||
(cl-check-type stderr (or null buffer))
|
||||
(cl-check-type callback function)
|
||||
(lambda (process event)
|
||||
(cl-check-type process process)
|
||||
(cl-check-type event string)
|
||||
(unwind-protect
|
||||
(if (string-equal event "finished\n")
|
||||
(progn
|
||||
(when stderr (kill-buffer stderr))
|
||||
(with-current-buffer stdin
|
||||
(funcall callback stdout))
|
||||
(kill-buffer stdout))
|
||||
(when stderr (kill-buffer stdout))
|
||||
(message "clang-include-fixer failed")
|
||||
(with-current-buffer (or stderr stdout)
|
||||
(insert "\nProcess " (process-name process)
|
||||
?\s event))
|
||||
(display-buffer (or stderr stdout))))
|
||||
nil))
|
||||
|
||||
(defun clang-include-fixer--replace-buffer (stdout)
|
||||
"Replace current buffer by content of STDOUT."
|
||||
(cl-check-type stdout buffer)
|
||||
(barf-if-buffer-read-only)
|
||||
(cond ((fboundp 'replace-buffer-contents) (replace-buffer-contents stdout))
|
||||
((clang-include-fixer--insert-line stdout (current-buffer)))
|
||||
(t (erase-buffer) (insert-buffer-substring stdout)))
|
||||
(message "Fix applied")
|
||||
nil)
|
||||
|
||||
(defun clang-include-fixer--insert-line (from to)
|
||||
"Insert a single missing line from the buffer FROM into TO.
|
||||
FROM and TO must be buffers. If the contents of FROM and TO are
|
||||
equal, do nothing and return non-nil. If FROM contains a single
|
||||
line missing from TO, insert that line into TO so that the buffer
|
||||
contents are equal and return non-nil. Otherwise, do nothing and
|
||||
return nil. Buffer restrictions are ignored."
|
||||
(cl-check-type from buffer)
|
||||
(cl-check-type to buffer)
|
||||
(with-current-buffer from
|
||||
(save-excursion
|
||||
(save-restriction
|
||||
(widen)
|
||||
(with-current-buffer to
|
||||
(save-excursion
|
||||
(save-restriction
|
||||
(widen)
|
||||
;; Search for the first buffer difference.
|
||||
(let ((chars (abs (compare-buffer-substrings to nil nil from nil nil))))
|
||||
(if (zerop chars)
|
||||
;; Buffer contents are equal, nothing to do.
|
||||
t
|
||||
(goto-char chars)
|
||||
;; We might have ended up in the middle of a line if the
|
||||
;; current line partially matches. In this case we would
|
||||
;; have to insert more than a line. Move to the beginning of
|
||||
;; the line to avoid this situation.
|
||||
(beginning-of-line)
|
||||
(with-current-buffer from
|
||||
(goto-char chars)
|
||||
(beginning-of-line)
|
||||
(let ((from-begin (point))
|
||||
(from-end (progn (forward-line) (point)))
|
||||
(to-point (with-current-buffer to (point))))
|
||||
;; Search for another buffer difference after the line in
|
||||
;; question. If there is none, we can proceed.
|
||||
(when (zerop (compare-buffer-substrings from from-end nil
|
||||
to to-point nil))
|
||||
(with-current-buffer to
|
||||
(insert-buffer-substring from from-begin from-end))
|
||||
t))))))))))))
|
||||
|
||||
(defun clang-include-fixer--add-header (stdout)
|
||||
"Analyse the result of clang-include-fixer stored in STDOUT.
|
||||
Add a missing header if there is any. If there are multiple
|
||||
possible headers the user can select one of them to be included.
|
||||
Temporarily highlight the affected symbols. Asynchronously call
|
||||
clang-include-fixer to insert the selected header."
|
||||
(cl-check-type stdout buffer-live)
|
||||
(let ((context (clang-include-fixer--parse-json stdout)))
|
||||
(let-alist context
|
||||
(cond
|
||||
((null .QuerySymbolInfos)
|
||||
(message "The file is fine, no need to add a header."))
|
||||
((null .HeaderInfos)
|
||||
(message "Couldn't find header for '%s'"
|
||||
(let-alist (car .QuerySymbolInfos) .RawIdentifier)))
|
||||
(t
|
||||
;; Users may C-g in prompts, make sure the process sentinel
|
||||
;; behaves correctly.
|
||||
(with-local-quit
|
||||
;; Replace the HeaderInfos list by a single header selected by
|
||||
;; the user.
|
||||
(clang-include-fixer--select-header context)
|
||||
;; Call clang-include-fixer again to insert the selected header.
|
||||
(clang-include-fixer--start
|
||||
(let ((old-tick (buffer-chars-modified-tick)))
|
||||
(lambda (stdout)
|
||||
(when (/= old-tick (buffer-chars-modified-tick))
|
||||
;; Replacing the buffer now would undo the user’s changes.
|
||||
(user-error (concat "The buffer has been changed "
|
||||
"before the header could be inserted")))
|
||||
(clang-include-fixer--replace-buffer stdout)
|
||||
(let-alist context
|
||||
(let-alist (car .HeaderInfos)
|
||||
(with-local-quit
|
||||
(run-hook-with-args 'clang-include-fixer-add-include-hook
|
||||
(substring .Header 1 -1)
|
||||
(string= (substring .Header 0 1) "<")))))))
|
||||
(format "-insert-header=%s"
|
||||
(clang-include-fixer--encode-json context))))))))
|
||||
nil)
|
||||
|
||||
(defun clang-include-fixer--select-header (context)
|
||||
"Prompt the user for a header if necessary.
|
||||
CONTEXT must be a clang-include-fixer context object in
|
||||
association list format. If it contains more than one HeaderInfo
|
||||
element, prompt the user to select one of the headers. CONTEXT
|
||||
is modified to include only the selected element."
|
||||
(cl-check-type context cons)
|
||||
(let-alist context
|
||||
(if (cdr .HeaderInfos)
|
||||
(clang-include-fixer--prompt-for-header context)
|
||||
(message "Only one include is missing: %s"
|
||||
(let-alist (car .HeaderInfos) .Header))))
|
||||
nil)
|
||||
|
||||
(defvar clang-include-fixer--history nil
|
||||
"History for `clang-include-fixer--prompt-for-header'.")
|
||||
|
||||
(defun clang-include-fixer--prompt-for-header (context)
|
||||
"Prompt the user for a single header.
|
||||
The choices are taken from the HeaderInfo elements in CONTEXT.
|
||||
They are replaced by the single element selected by the user."
|
||||
(let-alist context
|
||||
(let ((symbol (clang-include-fixer--symbol-name .QuerySymbolInfos))
|
||||
;; Add temporary highlighting so that the user knows which
|
||||
;; symbols the current session is about.
|
||||
(overlays (remove nil
|
||||
(mapcar #'clang-include-fixer--highlight .QuerySymbolInfos))))
|
||||
(unwind-protect
|
||||
(save-excursion
|
||||
;; While prompting, go to the closest overlay so that the user sees
|
||||
;; some context.
|
||||
(when overlays
|
||||
(goto-char (clang-include-fixer--closest-overlay overlays)))
|
||||
(cl-flet ((header (info) (let-alist info .Header)))
|
||||
;; The header-infos is already sorted by clang-include-fixer.
|
||||
(let* ((headers (mapcar #'header .HeaderInfos))
|
||||
(header (completing-read
|
||||
(clang-include-fixer--format-message
|
||||
"Select include for '%s': " symbol)
|
||||
headers nil :require-match nil
|
||||
'clang-include-fixer--history
|
||||
;; Specify a default to prevent the behavior
|
||||
;; described in
|
||||
;; https://github.com/DarwinAwardWinner/ido-completing-read-plus#why-does-ret-sometimes-not-select-the-first-completion-on-the-list--why-is-there-an-empty-entry-at-the-beginning-of-the-completion-list--what-happened-to-old-style-default-selection.
|
||||
(car headers)))
|
||||
(info (cl-find header .HeaderInfos :key #'header :test #'string=)))
|
||||
(unless info (user-error "No header selected"))
|
||||
(setcar .HeaderInfos info)
|
||||
(setcdr .HeaderInfos nil))))
|
||||
(mapc #'delete-overlay overlays)))))
|
||||
|
||||
(defun clang-include-fixer--symbol-name (symbol-infos)
|
||||
"Return the unique symbol name in SYMBOL-INFOS.
|
||||
Raise a signal if the symbol name is not unique."
|
||||
(let ((symbols (delete-dups (mapcar (lambda (info)
|
||||
(let-alist info .RawIdentifier))
|
||||
symbol-infos))))
|
||||
(when (cdr symbols)
|
||||
(error "Multiple symbols %s returned" symbols))
|
||||
(car symbols)))
|
||||
|
||||
(defun clang-include-fixer--highlight (symbol-info)
|
||||
"Add an overlay to highlight SYMBOL-INFO, if it points to a non-empty range.
|
||||
Return the overlay object, or nil."
|
||||
(let-alist symbol-info
|
||||
(unless (zerop .Range.Length)
|
||||
(let ((overlay (make-overlay
|
||||
(clang-include-fixer--filepos-to-bufferpos
|
||||
.Range.Offset 'approximate)
|
||||
(clang-include-fixer--filepos-to-bufferpos
|
||||
(+ .Range.Offset .Range.Length) 'approximate))))
|
||||
(overlay-put overlay 'face 'clang-include-fixer-highlight)
|
||||
overlay))))
|
||||
|
||||
(defun clang-include-fixer--closest-overlay (overlays)
|
||||
"Return the start of the overlay in OVERLAYS that is closest to point."
|
||||
(cl-check-type overlays cons)
|
||||
(let ((point (point))
|
||||
acc)
|
||||
(dolist (overlay overlays acc)
|
||||
(let ((start (overlay-start overlay)))
|
||||
(when (or (null acc) (< (abs (- point start)) (abs (- point acc))))
|
||||
(setq acc start))))))
|
||||
|
||||
(defun clang-include-fixer--parse-json (buffer)
|
||||
"Parse a JSON response from clang-include-fixer in BUFFER.
|
||||
Return the JSON object as an association list."
|
||||
(with-current-buffer buffer
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(let ((json-object-type 'alist)
|
||||
(json-array-type 'list)
|
||||
(json-key-type 'symbol)
|
||||
(json-false :json-false)
|
||||
(json-null nil)
|
||||
(json-pre-element-read-function nil)
|
||||
(json-post-element-read-function nil))
|
||||
(json-read)))))
|
||||
|
||||
(defun clang-include-fixer--encode-json (object)
|
||||
"Return the JSON representation of OBJECT as a string."
|
||||
(let ((json-encoding-separator ",")
|
||||
(json-encoding-default-indentation " ")
|
||||
(json-encoding-pretty-print nil)
|
||||
(json-encoding-lisp-style-closings nil)
|
||||
(json-encoding-object-sort-predicate nil))
|
||||
(json-encode object)))
|
||||
|
||||
(defun clang-include-fixer--symbol-at-point ()
|
||||
"Return the qualified symbol at point.
|
||||
If there is no symbol at point, return nil."
|
||||
;; Let ‘bounds-of-thing-at-point’ to do the hard work and deal with edge
|
||||
;; cases.
|
||||
(let ((bounds (bounds-of-thing-at-point 'symbol)))
|
||||
(when bounds
|
||||
(let ((beg (car bounds))
|
||||
(end (cdr bounds)))
|
||||
(save-excursion
|
||||
;; Extend the symbol range to the left. Skip over namespace
|
||||
;; delimiters and parent namespace names.
|
||||
(goto-char beg)
|
||||
(while (and (clang-include-fixer--skip-double-colon-backward)
|
||||
(skip-syntax-backward "w_")))
|
||||
;; Skip over one more namespace delimiter, for absolute names.
|
||||
(clang-include-fixer--skip-double-colon-backward)
|
||||
(setq beg (point))
|
||||
;; Extend the symbol range to the right. Skip over namespace
|
||||
;; delimiters and child namespace names.
|
||||
(goto-char end)
|
||||
(while (and (clang-include-fixer--skip-double-colon-forward)
|
||||
(skip-syntax-forward "w_")))
|
||||
(setq end (point)))
|
||||
(buffer-substring-no-properties beg end)))))
|
||||
|
||||
(defun clang-include-fixer--skip-double-colon-forward ()
|
||||
"Skip a double colon.
|
||||
When the next two characters are '::', skip them and return
|
||||
non-nil. Otherwise return nil."
|
||||
(let ((end (+ (point) 2)))
|
||||
(when (and (<= end (point-max))
|
||||
(string-equal (buffer-substring-no-properties (point) end) "::"))
|
||||
(goto-char end)
|
||||
t)))
|
||||
|
||||
(defun clang-include-fixer--skip-double-colon-backward ()
|
||||
"Skip a double colon.
|
||||
When the previous two characters are '::', skip them and return
|
||||
non-nil. Otherwise return nil."
|
||||
(let ((beg (- (point) 2)))
|
||||
(when (and (>= beg (point-min))
|
||||
(string-equal (buffer-substring-no-properties beg (point)) "::"))
|
||||
(goto-char beg)
|
||||
t)))
|
||||
|
||||
;; ‘filepos-to-bufferpos’ is new in Emacs 25.1. Provide a fallback for older
|
||||
;; versions.
|
||||
(defalias 'clang-include-fixer--filepos-to-bufferpos
|
||||
(if (fboundp 'filepos-to-bufferpos)
|
||||
'filepos-to-bufferpos
|
||||
(lambda (byte &optional _quality _coding-system)
|
||||
(byte-to-position (1+ byte)))))
|
||||
|
||||
;; ‘format-message’ is new in Emacs 25.1. Provide a fallback for older
|
||||
;; versions.
|
||||
(defalias 'clang-include-fixer--format-message
|
||||
(if (fboundp 'format-message) 'format-message 'format))
|
||||
|
||||
;; ‘file-local-name’ is new in Emacs 26.1. Provide a fallback for older
|
||||
;; versions.
|
||||
(defalias 'clang-include-fixer--file-local-name
|
||||
(if (fboundp 'file-local-name) #'file-local-name
|
||||
(lambda (file) (or (file-remote-p file 'localname) file))))
|
||||
|
||||
(provide 'clang-include-fixer)
|
||||
;;; clang-include-fixer.el ends here
|
||||
@ -0,0 +1,243 @@
|
||||
# This file is a minimal clang-include-fixer vim-integration. To install:
|
||||
# - Change 'binary' if clang-include-fixer is not on the path (see below).
|
||||
# - Add to your .vimrc:
|
||||
#
|
||||
# noremap <leader>cf :pyf path/to/llvm/source/tools/clang/tools/extra/clang-include-fixer/tool/clang-include-fixer.py<cr>
|
||||
#
|
||||
# This enables clang-include-fixer for NORMAL and VISUAL mode. Change
|
||||
# "<leader>cf" to another binding if you need clang-include-fixer on a
|
||||
# different key.
|
||||
#
|
||||
# To set up clang-include-fixer, see
|
||||
# http://clang.llvm.org/extra/clang-include-fixer.html
|
||||
#
|
||||
# With this integration you can press the bound key and clang-include-fixer will
|
||||
# be run on the current buffer.
|
||||
#
|
||||
# It operates on the current, potentially unsaved buffer and does not create
|
||||
# or save any files. To revert a fix, just undo.
|
||||
|
||||
from __future__ import print_function
|
||||
import argparse
|
||||
import difflib
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import vim
|
||||
|
||||
# set g:clang_include_fixer_path to the path to clang-include-fixer if it is not
|
||||
# on the path.
|
||||
# Change this to the full path if clang-include-fixer is not on the path.
|
||||
binary = "clang-include-fixer"
|
||||
if vim.eval('exists("g:clang_include_fixer_path")') == "1":
|
||||
binary = vim.eval("g:clang_include_fixer_path")
|
||||
|
||||
maximum_suggested_headers = 3
|
||||
if vim.eval('exists("g:clang_include_fixer_maximum_suggested_headers")') == "1":
|
||||
maximum_suggested_headers = max(
|
||||
1, vim.eval("g:clang_include_fixer_maximum_suggested_headers")
|
||||
)
|
||||
|
||||
increment_num = 5
|
||||
if vim.eval('exists("g:clang_include_fixer_increment_num")') == "1":
|
||||
increment_num = max(1, vim.eval("g:clang_include_fixer_increment_num"))
|
||||
|
||||
jump_to_include = False
|
||||
if vim.eval('exists("g:clang_include_fixer_jump_to_include")') == "1":
|
||||
jump_to_include = vim.eval("g:clang_include_fixer_jump_to_include") != "0"
|
||||
|
||||
query_mode = False
|
||||
if vim.eval('exists("g:clang_include_fixer_query_mode")') == "1":
|
||||
query_mode = vim.eval("g:clang_include_fixer_query_mode") != "0"
|
||||
|
||||
|
||||
def GetUserSelection(message, headers, maximum_suggested_headers):
|
||||
eval_message = message + "\n"
|
||||
for idx, header in enumerate(headers[0:maximum_suggested_headers]):
|
||||
eval_message += "({0}). {1}\n".format(idx + 1, header)
|
||||
eval_message += "Enter (q) to quit;"
|
||||
if maximum_suggested_headers < len(headers):
|
||||
eval_message += " (m) to show {0} more candidates.".format(
|
||||
min(increment_num, len(headers) - maximum_suggested_headers)
|
||||
)
|
||||
|
||||
eval_message += "\nSelect (default 1): "
|
||||
res = vim.eval("input('{0}')".format(eval_message))
|
||||
if res == "":
|
||||
# choose the top ranked header by default
|
||||
idx = 1
|
||||
elif res == "q":
|
||||
raise Exception(" Insertion cancelled...")
|
||||
elif res == "m":
|
||||
return GetUserSelection(
|
||||
message, headers, maximum_suggested_headers + increment_num
|
||||
)
|
||||
else:
|
||||
try:
|
||||
idx = int(res)
|
||||
if idx <= 0 or idx > len(headers):
|
||||
raise Exception()
|
||||
except Exception:
|
||||
# Show a new prompt on invalid option instead of aborting so that users
|
||||
# don't need to wait for another clang-include-fixer run.
|
||||
print("Invalid option: {}".format(res), file=sys.stderr)
|
||||
return GetUserSelection(message, headers, maximum_suggested_headers)
|
||||
return headers[idx - 1]
|
||||
|
||||
|
||||
def execute(command, text):
|
||||
# Avoid flashing a cmd prompt on Windows.
|
||||
startupinfo = None
|
||||
if sys.platform.startswith("win32"):
|
||||
startupinfo = subprocess.STARTUPINFO()
|
||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
startupinfo.wShowWindow = subprocess.SW_HIDE
|
||||
|
||||
p = subprocess.Popen(
|
||||
command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
stdin=subprocess.PIPE,
|
||||
startupinfo=startupinfo,
|
||||
)
|
||||
return p.communicate(input=text.encode("utf-8"))
|
||||
|
||||
|
||||
def InsertHeaderToVimBuffer(header, text):
|
||||
command = [
|
||||
binary,
|
||||
"-stdin",
|
||||
"-insert-header=" + json.dumps(header),
|
||||
vim.current.buffer.name,
|
||||
]
|
||||
stdout, stderr = execute(command, text)
|
||||
if stderr:
|
||||
raise Exception(stderr)
|
||||
if stdout:
|
||||
lines = stdout.splitlines()
|
||||
sequence = difflib.SequenceMatcher(None, vim.current.buffer, lines)
|
||||
line_num = None
|
||||
for op in reversed(sequence.get_opcodes()):
|
||||
if op[0] != "equal":
|
||||
vim.current.buffer[op[1] : op[2]] = lines[op[3] : op[4]]
|
||||
if op[0] == "insert":
|
||||
# line_num in vim is 1-based.
|
||||
line_num = op[1] + 1
|
||||
|
||||
if jump_to_include and line_num:
|
||||
vim.current.window.cursor = (line_num, 0)
|
||||
|
||||
|
||||
# The vim internal implementation (expand("cword"/"cWORD")) doesn't support
|
||||
# our use case very well, we re-implement our own one.
|
||||
def get_symbol_under_cursor():
|
||||
line = vim.eval('line(".")')
|
||||
# column number in vim is 1-based.
|
||||
col = int(vim.eval('col(".")')) - 1
|
||||
line_text = vim.eval("getline({0})".format(line))
|
||||
if len(line_text) == 0:
|
||||
return ""
|
||||
symbol_pos_begin = col
|
||||
p = re.compile("[a-zA-Z0-9:_]")
|
||||
while symbol_pos_begin >= 0 and p.match(line_text[symbol_pos_begin]):
|
||||
symbol_pos_begin -= 1
|
||||
|
||||
symbol_pos_end = col
|
||||
while symbol_pos_end < len(line_text) and p.match(line_text[symbol_pos_end]):
|
||||
symbol_pos_end += 1
|
||||
return line_text[symbol_pos_begin + 1 : symbol_pos_end]
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Vim integration for clang-include-fixer"
|
||||
)
|
||||
parser.add_argument("-db", default="yaml", help="clang-include-fixer input format.")
|
||||
parser.add_argument("-input", default="", help="String to initialize the database.")
|
||||
# Don't throw exception when parsing unknown arguments to make the script
|
||||
# work in neovim.
|
||||
# Neovim (at least v0.2.1) somehow mangles the sys.argv in a weird way: it
|
||||
# will pass additional arguments (e.g. "-c script_host.py") to sys.argv,
|
||||
# which makes the script fail.
|
||||
args, _ = parser.parse_known_args()
|
||||
|
||||
# Get the current text.
|
||||
buf = vim.current.buffer
|
||||
text = "\n".join(buf)
|
||||
|
||||
if query_mode:
|
||||
symbol = get_symbol_under_cursor()
|
||||
if len(symbol) == 0:
|
||||
print("Skip querying empty symbol.")
|
||||
return
|
||||
command = [
|
||||
binary,
|
||||
"-stdin",
|
||||
"-query-symbol=" + get_symbol_under_cursor(),
|
||||
"-db=" + args.db,
|
||||
"-input=" + args.input,
|
||||
vim.current.buffer.name,
|
||||
]
|
||||
else:
|
||||
# Run command to get all headers.
|
||||
command = [
|
||||
binary,
|
||||
"-stdin",
|
||||
"-output-headers",
|
||||
"-db=" + args.db,
|
||||
"-input=" + args.input,
|
||||
vim.current.buffer.name,
|
||||
]
|
||||
stdout, stderr = execute(command, text)
|
||||
if stderr:
|
||||
print(
|
||||
"Error while running clang-include-fixer: {}".format(stderr),
|
||||
file=sys.stderr,
|
||||
)
|
||||
return
|
||||
|
||||
include_fixer_context = json.loads(stdout)
|
||||
query_symbol_infos = include_fixer_context["QuerySymbolInfos"]
|
||||
if not query_symbol_infos:
|
||||
print("The file is fine, no need to add a header.")
|
||||
return
|
||||
symbol = query_symbol_infos[0]["RawIdentifier"]
|
||||
# The header_infos is already sorted by clang-include-fixer.
|
||||
header_infos = include_fixer_context["HeaderInfos"]
|
||||
# Deduplicate headers while keeping the order, so that the same header would
|
||||
# not be suggested twice.
|
||||
unique_headers = []
|
||||
seen = set()
|
||||
for header_info in header_infos:
|
||||
header = header_info["Header"]
|
||||
if header not in seen:
|
||||
seen.add(header)
|
||||
unique_headers.append(header)
|
||||
|
||||
if not unique_headers:
|
||||
print("Couldn't find a header for {0}.".format(symbol))
|
||||
return
|
||||
|
||||
try:
|
||||
selected = unique_headers[0]
|
||||
inserted_header_infos = header_infos
|
||||
if len(unique_headers) > 1:
|
||||
selected = GetUserSelection(
|
||||
"choose a header file for {0}.".format(symbol),
|
||||
unique_headers,
|
||||
maximum_suggested_headers,
|
||||
)
|
||||
inserted_header_infos = [
|
||||
header for header in header_infos if header["Header"] == selected
|
||||
]
|
||||
include_fixer_context["HeaderInfos"] = inserted_header_infos
|
||||
|
||||
InsertHeaderToVimBuffer(include_fixer_context, text)
|
||||
print("Added #include {0} for {1}.".format(selected, symbol))
|
||||
except Exception as error:
|
||||
print(error, file=sys.stderr)
|
||||
return
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -0,0 +1,79 @@
|
||||
;;; clang-rename.el --- Renames every occurrence of a symbol found at <offset>. -*- lexical-binding: t; -*-
|
||||
|
||||
;; Keywords: tools, c
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; To install clang-rename.el make sure the directory of this file is in your
|
||||
;; `load-path' and add
|
||||
;;
|
||||
;; (require 'clang-rename)
|
||||
;;
|
||||
;; to your .emacs configuration.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(defgroup clang-rename nil
|
||||
"Integration with clang-rename"
|
||||
:group 'c)
|
||||
|
||||
(defcustom clang-rename-binary "clang-rename"
|
||||
"Path to clang-rename executable."
|
||||
:type '(file :must-match t)
|
||||
:group 'clang-rename)
|
||||
|
||||
;;;###autoload
|
||||
(defun clang-rename (new-name)
|
||||
"Rename all instances of the symbol at point to NEW-NAME using clang-rename."
|
||||
(interactive "sEnter a new name: ")
|
||||
(save-some-buffers :all)
|
||||
;; clang-rename should not be combined with other operations when undoing.
|
||||
(undo-boundary)
|
||||
(let ((output-buffer (get-buffer-create "*clang-rename*")))
|
||||
(with-current-buffer output-buffer (erase-buffer))
|
||||
(let ((exit-code (call-process
|
||||
clang-rename-binary nil output-buffer nil
|
||||
(format "-offset=%d"
|
||||
;; clang-rename wants file (byte) offsets, not
|
||||
;; buffer (character) positions.
|
||||
(clang-rename--bufferpos-to-filepos
|
||||
;; Emacs treats one character after a symbol as
|
||||
;; part of the symbol, but clang-rename doesn’t.
|
||||
;; Use the beginning of the current symbol, if
|
||||
;; available, to resolve the inconsistency.
|
||||
(or (car (bounds-of-thing-at-point 'symbol))
|
||||
(point))
|
||||
'exact))
|
||||
(format "-new-name=%s" new-name)
|
||||
"-i" (buffer-file-name))))
|
||||
(if (and (integerp exit-code) (zerop exit-code))
|
||||
;; Success; revert current buffer so it gets the modifications.
|
||||
(progn
|
||||
(kill-buffer output-buffer)
|
||||
(revert-buffer :ignore-auto :noconfirm :preserve-modes))
|
||||
;; Failure; append exit code to output buffer and display it.
|
||||
(let ((message (clang-rename--format-message
|
||||
"clang-rename failed with %s %s"
|
||||
(if (integerp exit-code) "exit status" "signal")
|
||||
exit-code)))
|
||||
(with-current-buffer output-buffer
|
||||
(insert ?\n message ?\n))
|
||||
(message "%s" message)
|
||||
(display-buffer output-buffer))))))
|
||||
|
||||
(defalias 'clang-rename--bufferpos-to-filepos
|
||||
(if (fboundp 'bufferpos-to-filepos)
|
||||
'bufferpos-to-filepos
|
||||
;; Emacs 24 doesn’t have ‘bufferpos-to-filepos’, simulate it using
|
||||
;; ‘position-bytes’.
|
||||
(lambda (position &optional _quality _coding-system)
|
||||
(1- (position-bytes position)))))
|
||||
|
||||
;; ‘format-message’ is new in Emacs 25.1. Provide a fallback for older
|
||||
;; versions.
|
||||
(defalias 'clang-rename--format-message
|
||||
(if (fboundp 'format-message) 'format-message 'format))
|
||||
|
||||
(provide 'clang-rename)
|
||||
|
||||
;;; clang-rename.el ends here
|
||||
@ -0,0 +1,70 @@
|
||||
"""
|
||||
Minimal clang-rename integration with Vim.
|
||||
|
||||
Before installing make sure one of the following is satisfied:
|
||||
|
||||
* clang-rename is in your PATH
|
||||
* `g:clang_rename_path` in ~/.vimrc points to valid clang-rename executable
|
||||
* `binary` in clang-rename.py points to valid to clang-rename executable
|
||||
|
||||
To install, simply put this into your ~/.vimrc for python2 support
|
||||
|
||||
noremap <leader>cr :pyf <path-to>/clang-rename.py<cr>
|
||||
|
||||
For python3 use the following command (note the change from :pyf to :py3f)
|
||||
|
||||
noremap <leader>cr :py3f <path-to>/clang-rename.py<cr>
|
||||
|
||||
IMPORTANT NOTE: Before running the tool, make sure you saved the file.
|
||||
|
||||
All you have to do now is to place a cursor on a variable/function/class which
|
||||
you would like to rename and press '<leader>cr'. You will be prompted for a new
|
||||
name if the cursor points to a valid symbol.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
import vim
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
binary = "clang-rename"
|
||||
if vim.eval('exists("g:clang_rename_path")') == "1":
|
||||
binary = vim.eval("g:clang_rename_path")
|
||||
|
||||
# Get arguments for clang-rename binary.
|
||||
offset = int(vim.eval('line2byte(line("."))+col(".")')) - 2
|
||||
if offset < 0:
|
||||
print(
|
||||
"Couldn't determine cursor position. Is your file empty?", file=sys.stderr
|
||||
)
|
||||
return
|
||||
filename = vim.current.buffer.name
|
||||
|
||||
new_name_request_message = "type new name:"
|
||||
new_name = vim.eval("input('{}\n')".format(new_name_request_message))
|
||||
|
||||
# Call clang-rename.
|
||||
command = [
|
||||
binary,
|
||||
filename,
|
||||
"-i",
|
||||
"-offset",
|
||||
str(offset),
|
||||
"-new-name",
|
||||
str(new_name),
|
||||
]
|
||||
# FIXME: make it possible to run the tool on unsaved file.
|
||||
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
stdout, stderr = p.communicate()
|
||||
|
||||
if stderr:
|
||||
print(stderr)
|
||||
|
||||
# Reload all buffers in Vim.
|
||||
vim.command("checktime")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -0,0 +1,382 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# ===- clang-tidy-diff.py - ClangTidy Diff Checker -----------*- python -*--===#
|
||||
#
|
||||
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
# See https://llvm.org/LICENSE.txt for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
#
|
||||
# ===-----------------------------------------------------------------------===#
|
||||
|
||||
r"""
|
||||
ClangTidy Diff Checker
|
||||
======================
|
||||
|
||||
This script reads input from a unified diff, runs clang-tidy on all changed
|
||||
files and outputs clang-tidy warnings in changed lines only. This is useful to
|
||||
detect clang-tidy regressions in the lines touched by a specific patch.
|
||||
Example usage for git/svn users:
|
||||
|
||||
git diff -U0 HEAD^ | clang-tidy-diff.py -p1
|
||||
svn diff --diff-cmd=diff -x-U0 | \
|
||||
clang-tidy-diff.py -fix -checks=-*,modernize-use-override
|
||||
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import glob
|
||||
import json
|
||||
import multiprocessing
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
yaml = None
|
||||
|
||||
is_py2 = sys.version[0] == "2"
|
||||
|
||||
if is_py2:
|
||||
import Queue as queue
|
||||
else:
|
||||
import queue as queue
|
||||
|
||||
|
||||
def run_tidy(task_queue, lock, timeout, failed_files):
|
||||
watchdog = None
|
||||
while True:
|
||||
command = task_queue.get()
|
||||
try:
|
||||
proc = subprocess.Popen(
|
||||
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
|
||||
if timeout is not None:
|
||||
watchdog = threading.Timer(timeout, proc.kill)
|
||||
watchdog.start()
|
||||
|
||||
stdout, stderr = proc.communicate()
|
||||
if proc.returncode != 0:
|
||||
if proc.returncode < 0:
|
||||
msg = "Terminated by signal %d : %s\n" % (
|
||||
-proc.returncode,
|
||||
" ".join(command),
|
||||
)
|
||||
stderr += msg.encode("utf-8")
|
||||
failed_files.append(command)
|
||||
|
||||
with lock:
|
||||
sys.stdout.write(stdout.decode("utf-8") + "\n")
|
||||
sys.stdout.flush()
|
||||
if stderr:
|
||||
sys.stderr.write(stderr.decode("utf-8") + "\n")
|
||||
sys.stderr.flush()
|
||||
except Exception as e:
|
||||
with lock:
|
||||
sys.stderr.write("Failed: " + str(e) + ": ".join(command) + "\n")
|
||||
finally:
|
||||
with lock:
|
||||
if not (timeout is None or watchdog is None):
|
||||
if not watchdog.is_alive():
|
||||
sys.stderr.write(
|
||||
"Terminated by timeout: " + " ".join(command) + "\n"
|
||||
)
|
||||
watchdog.cancel()
|
||||
task_queue.task_done()
|
||||
|
||||
|
||||
def start_workers(max_tasks, tidy_caller, arguments):
|
||||
for _ in range(max_tasks):
|
||||
t = threading.Thread(target=tidy_caller, args=arguments)
|
||||
t.daemon = True
|
||||
t.start()
|
||||
|
||||
|
||||
def merge_replacement_files(tmpdir, mergefile):
|
||||
"""Merge all replacement files in a directory into a single file"""
|
||||
# The fixes suggested by clang-tidy >= 4.0.0 are given under
|
||||
# the top level key 'Diagnostics' in the output yaml files
|
||||
mergekey = "Diagnostics"
|
||||
merged = []
|
||||
for replacefile in glob.iglob(os.path.join(tmpdir, "*.yaml")):
|
||||
content = yaml.safe_load(open(replacefile, "r"))
|
||||
if not content:
|
||||
continue # Skip empty files.
|
||||
merged.extend(content.get(mergekey, []))
|
||||
|
||||
if merged:
|
||||
# MainSourceFile: The key is required by the definition inside
|
||||
# include/clang/Tooling/ReplacementsYaml.h, but the value
|
||||
# is actually never used inside clang-apply-replacements,
|
||||
# so we set it to '' here.
|
||||
output = {"MainSourceFile": "", mergekey: merged}
|
||||
with open(mergefile, "w") as out:
|
||||
yaml.safe_dump(output, out)
|
||||
else:
|
||||
# Empty the file:
|
||||
open(mergefile, "w").close()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Run clang-tidy against changed files, and "
|
||||
"output diagnostics only for modified "
|
||||
"lines."
|
||||
)
|
||||
parser.add_argument(
|
||||
"-clang-tidy-binary",
|
||||
metavar="PATH",
|
||||
default="clang-tidy",
|
||||
help="path to clang-tidy binary",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
metavar="NUM",
|
||||
default=0,
|
||||
help="strip the smallest prefix containing P slashes",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-regex",
|
||||
metavar="PATTERN",
|
||||
default=None,
|
||||
help="custom pattern selecting file paths to check "
|
||||
"(case sensitive, overrides -iregex)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-iregex",
|
||||
metavar="PATTERN",
|
||||
default=r".*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc)",
|
||||
help="custom pattern selecting file paths to check "
|
||||
"(case insensitive, overridden by -regex)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-j",
|
||||
type=int,
|
||||
default=1,
|
||||
help="number of tidy instances to be run in parallel.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-timeout", type=int, default=None, help="timeout per each file in seconds."
|
||||
)
|
||||
parser.add_argument(
|
||||
"-fix", action="store_true", default=False, help="apply suggested fixes"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-checks",
|
||||
help="checks filter, when not specified, use clang-tidy " "default",
|
||||
default="",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-config-file",
|
||||
dest="config_file",
|
||||
help="Specify the path of .clang-tidy or custom config file",
|
||||
default="",
|
||||
)
|
||||
parser.add_argument("-use-color", action="store_true", help="Use colors in output")
|
||||
parser.add_argument(
|
||||
"-path", dest="build_path", help="Path used to read a compile command database."
|
||||
)
|
||||
if yaml:
|
||||
parser.add_argument(
|
||||
"-export-fixes",
|
||||
metavar="FILE_OR_DIRECTORY",
|
||||
dest="export_fixes",
|
||||
help="A directory or a yaml file to store suggested fixes in, "
|
||||
"which can be applied with clang-apply-replacements. If the "
|
||||
"parameter is a directory, the fixes of each compilation unit are "
|
||||
"stored in individual yaml files in the directory.",
|
||||
)
|
||||
else:
|
||||
parser.add_argument(
|
||||
"-export-fixes",
|
||||
metavar="DIRECTORY",
|
||||
dest="export_fixes",
|
||||
help="A directory to store suggested fixes in, which can be applied "
|
||||
"with clang-apply-replacements. The fixes of each compilation unit are "
|
||||
"stored in individual yaml files in the directory.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-extra-arg",
|
||||
dest="extra_arg",
|
||||
action="append",
|
||||
default=[],
|
||||
help="Additional argument to append to the compiler " "command line.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-extra-arg-before",
|
||||
dest="extra_arg_before",
|
||||
action="append",
|
||||
default=[],
|
||||
help="Additional argument to prepend to the compiler " "command line.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-quiet",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Run clang-tidy in quiet mode",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-load",
|
||||
dest="plugins",
|
||||
action="append",
|
||||
default=[],
|
||||
help="Load the specified plugin in clang-tidy.",
|
||||
)
|
||||
|
||||
clang_tidy_args = []
|
||||
argv = sys.argv[1:]
|
||||
if "--" in argv:
|
||||
clang_tidy_args.extend(argv[argv.index("--") :])
|
||||
argv = argv[: argv.index("--")]
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
# Extract changed lines for each file.
|
||||
filename = None
|
||||
lines_by_file = {}
|
||||
for line in sys.stdin:
|
||||
match = re.search('^\+\+\+\ "?(.*?/){%s}([^ \t\n"]*)' % args.p, line)
|
||||
if match:
|
||||
filename = match.group(2)
|
||||
if filename is None:
|
||||
continue
|
||||
|
||||
if args.regex is not None:
|
||||
if not re.match("^%s$" % args.regex, filename):
|
||||
continue
|
||||
else:
|
||||
if not re.match("^%s$" % args.iregex, filename, re.IGNORECASE):
|
||||
continue
|
||||
|
||||
match = re.search("^@@.*\+(\d+)(,(\d+))?", line)
|
||||
if match:
|
||||
start_line = int(match.group(1))
|
||||
line_count = 1
|
||||
if match.group(3):
|
||||
line_count = int(match.group(3))
|
||||
if line_count == 0:
|
||||
continue
|
||||
end_line = start_line + line_count - 1
|
||||
lines_by_file.setdefault(filename, []).append([start_line, end_line])
|
||||
|
||||
if not any(lines_by_file):
|
||||
print("No relevant changes found.")
|
||||
sys.exit(0)
|
||||
|
||||
max_task_count = args.j
|
||||
if max_task_count == 0:
|
||||
max_task_count = multiprocessing.cpu_count()
|
||||
max_task_count = min(len(lines_by_file), max_task_count)
|
||||
|
||||
combine_fixes = False
|
||||
export_fixes_dir = None
|
||||
delete_fixes_dir = False
|
||||
if args.export_fixes is not None:
|
||||
# if a directory is given, create it if it does not exist
|
||||
if args.export_fixes.endswith(os.path.sep) and not os.path.isdir(
|
||||
args.export_fixes
|
||||
):
|
||||
os.makedirs(args.export_fixes)
|
||||
|
||||
if not os.path.isdir(args.export_fixes):
|
||||
if not yaml:
|
||||
raise RuntimeError(
|
||||
"Cannot combine fixes in one yaml file. Either install PyYAML or specify an output directory."
|
||||
)
|
||||
|
||||
combine_fixes = True
|
||||
|
||||
if os.path.isdir(args.export_fixes):
|
||||
export_fixes_dir = args.export_fixes
|
||||
|
||||
if combine_fixes:
|
||||
export_fixes_dir = tempfile.mkdtemp()
|
||||
delete_fixes_dir = True
|
||||
|
||||
# Tasks for clang-tidy.
|
||||
task_queue = queue.Queue(max_task_count)
|
||||
# A lock for console output.
|
||||
lock = threading.Lock()
|
||||
|
||||
# List of files with a non-zero return code.
|
||||
failed_files = []
|
||||
|
||||
# Run a pool of clang-tidy workers.
|
||||
start_workers(
|
||||
max_task_count, run_tidy, (task_queue, lock, args.timeout, failed_files)
|
||||
)
|
||||
|
||||
# Form the common args list.
|
||||
common_clang_tidy_args = []
|
||||
if args.fix:
|
||||
common_clang_tidy_args.append("-fix")
|
||||
if args.checks != "":
|
||||
common_clang_tidy_args.append("-checks=" + args.checks)
|
||||
if args.config_file != "":
|
||||
common_clang_tidy_args.append("-config-file=" + args.config_file)
|
||||
if args.quiet:
|
||||
common_clang_tidy_args.append("-quiet")
|
||||
if args.build_path is not None:
|
||||
common_clang_tidy_args.append("-p=%s" % args.build_path)
|
||||
if args.use_color:
|
||||
common_clang_tidy_args.append("--use-color")
|
||||
for arg in args.extra_arg:
|
||||
common_clang_tidy_args.append("-extra-arg=%s" % arg)
|
||||
for arg in args.extra_arg_before:
|
||||
common_clang_tidy_args.append("-extra-arg-before=%s" % arg)
|
||||
for plugin in args.plugins:
|
||||
common_clang_tidy_args.append("-load=%s" % plugin)
|
||||
|
||||
for name in lines_by_file:
|
||||
line_filter_json = json.dumps(
|
||||
[{"name": name, "lines": lines_by_file[name]}], separators=(",", ":")
|
||||
)
|
||||
|
||||
# Run clang-tidy on files containing changes.
|
||||
command = [args.clang_tidy_binary]
|
||||
command.append("-line-filter=" + line_filter_json)
|
||||
if args.export_fixes is not None:
|
||||
# Get a temporary file. We immediately close the handle so clang-tidy can
|
||||
# overwrite it.
|
||||
(handle, tmp_name) = tempfile.mkstemp(suffix=".yaml", dir=export_fixes_dir)
|
||||
os.close(handle)
|
||||
command.append("-export-fixes=" + tmp_name)
|
||||
command.extend(common_clang_tidy_args)
|
||||
command.append(name)
|
||||
command.extend(clang_tidy_args)
|
||||
|
||||
task_queue.put(command)
|
||||
|
||||
# Application return code
|
||||
return_code = 0
|
||||
|
||||
# Wait for all threads to be done.
|
||||
task_queue.join()
|
||||
# Application return code
|
||||
return_code = 0
|
||||
if failed_files:
|
||||
return_code = 1
|
||||
|
||||
if combine_fixes:
|
||||
print("Writing fixes to " + args.export_fixes + " ...")
|
||||
try:
|
||||
merge_replacement_files(export_fixes_dir, args.export_fixes)
|
||||
except:
|
||||
sys.stderr.write("Error exporting fixes.\n")
|
||||
traceback.print_exc()
|
||||
return_code = 1
|
||||
|
||||
if delete_fixes_dir:
|
||||
shutil.rmtree(export_fixes_dir)
|
||||
sys.exit(return_code)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -0,0 +1,87 @@
|
||||
// Append using posix-style a file name or directory to Base
|
||||
function append(Base, New) {
|
||||
if (!New)
|
||||
return Base;
|
||||
if (Base)
|
||||
Base += "/";
|
||||
Base += New;
|
||||
return Base;
|
||||
}
|
||||
|
||||
// Get relative path to access FilePath from CurrentDirectory
|
||||
function computeRelativePath(FilePath, CurrentDirectory) {
|
||||
var Path = FilePath;
|
||||
while (Path) {
|
||||
if (CurrentDirectory == Path)
|
||||
return FilePath.substring(Path.length + 1);
|
||||
Path = Path.substring(0, Path.lastIndexOf("/"));
|
||||
}
|
||||
|
||||
var Dir = CurrentDirectory;
|
||||
var Result = "";
|
||||
while (Dir) {
|
||||
if (Dir == FilePath)
|
||||
break;
|
||||
Dir = Dir.substring(0, Dir.lastIndexOf("/"));
|
||||
Result = append(Result, "..")
|
||||
}
|
||||
Result = append(Result, FilePath.substring(Dir.length))
|
||||
return Result;
|
||||
}
|
||||
|
||||
function genLink(Ref, CurrentDirectory) {
|
||||
var Path = computeRelativePath(Ref.Path, CurrentDirectory);
|
||||
if (Ref.RefType == "namespace")
|
||||
Path = append(Path, "index.html");
|
||||
else
|
||||
Path = append(Path, Ref.Name + ".html")
|
||||
|
||||
ANode = document.createElement("a");
|
||||
ANode.setAttribute("href", Path);
|
||||
var TextNode = document.createTextNode(Ref.Name);
|
||||
ANode.appendChild(TextNode);
|
||||
return ANode;
|
||||
}
|
||||
|
||||
function genHTMLOfIndex(Index, CurrentDirectory, IsOutermostList) {
|
||||
// Out will store the HTML elements that Index requires to be generated
|
||||
var Out = [];
|
||||
if (Index.Name) {
|
||||
var SpanNode = document.createElement("span");
|
||||
var TextNode = document.createTextNode(Index.Name);
|
||||
SpanNode.appendChild(genLink(Index, CurrentDirectory));
|
||||
Out.push(SpanNode);
|
||||
}
|
||||
if (Index.Children.length == 0)
|
||||
return Out;
|
||||
// Only the outermost list should use ol, the others should use ul
|
||||
var ListNodeName = IsOutermostList ? "ol" : "ul";
|
||||
var ListNode = document.createElement(ListNodeName);
|
||||
for (Child of Index.Children) {
|
||||
var LiNode = document.createElement("li");
|
||||
ChildNodes = genHTMLOfIndex(Child, CurrentDirectory, false);
|
||||
for (Node of ChildNodes)
|
||||
LiNode.appendChild(Node);
|
||||
ListNode.appendChild(LiNode);
|
||||
}
|
||||
Out.push(ListNode);
|
||||
return Out;
|
||||
}
|
||||
|
||||
function createIndex(Index) {
|
||||
// Get the DOM element where the index will be created
|
||||
var IndexDiv = document.getElementById("sidebar-left");
|
||||
// Get the relative path of this file
|
||||
CurrentDirectory = IndexDiv.getAttribute("path");
|
||||
var IndexNodes = genHTMLOfIndex(Index, CurrentDirectory, true);
|
||||
for (Node of IndexNodes)
|
||||
IndexDiv.appendChild(Node);
|
||||
}
|
||||
|
||||
// Runs after DOM loads
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
// JsonIndex is a variable from another file that contains the index
|
||||
// in JSON format
|
||||
var Index = JSON.parse(JsonIndex);
|
||||
createIndex(Index);
|
||||
});
|
||||
@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# =- run-find-all-symbols.py - Parallel find-all-symbols runner -*- python -*-=#
|
||||
#
|
||||
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
# See https://llvm.org/LICENSE.txt for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
#
|
||||
# ===------------------------------------------------------------------------===#
|
||||
|
||||
"""
|
||||
Parallel find-all-symbols runner
|
||||
================================
|
||||
|
||||
Runs find-all-symbols over all files in a compilation database.
|
||||
|
||||
Example invocations.
|
||||
- Run find-all-symbols on all files in the current working directory.
|
||||
run-find-all-symbols.py <source-file>
|
||||
|
||||
Compilation database setup:
|
||||
http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import multiprocessing
|
||||
import os
|
||||
import Queue
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
|
||||
|
||||
def find_compilation_database(path):
|
||||
"""Adjusts the directory until a compilation database is found."""
|
||||
result = "./"
|
||||
while not os.path.isfile(os.path.join(result, path)):
|
||||
if os.path.realpath(result) == "/":
|
||||
print("Error: could not find compilation database.")
|
||||
sys.exit(1)
|
||||
result += "../"
|
||||
return os.path.realpath(result)
|
||||
|
||||
|
||||
def MergeSymbols(directory, args):
|
||||
"""Merge all symbol files (yaml) in a given directory into a single file."""
|
||||
invocation = [args.binary, "-merge-dir=" + directory, args.saving_path]
|
||||
subprocess.call(invocation)
|
||||
print("Merge is finished. Saving results in " + args.saving_path)
|
||||
|
||||
|
||||
def run_find_all_symbols(args, tmpdir, build_path, queue):
|
||||
"""Takes filenames out of queue and runs find-all-symbols on them."""
|
||||
while True:
|
||||
name = queue.get()
|
||||
invocation = [args.binary, name, "-output-dir=" + tmpdir, "-p=" + build_path]
|
||||
sys.stdout.write(" ".join(invocation) + "\n")
|
||||
subprocess.call(invocation)
|
||||
queue.task_done()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Runs find-all-symbols over all" "files in a compilation database."
|
||||
)
|
||||
parser.add_argument(
|
||||
"-binary",
|
||||
metavar="PATH",
|
||||
default="./bin/find-all-symbols",
|
||||
help="path to find-all-symbols binary",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-j", type=int, default=0, help="number of instances to be run in parallel."
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p", dest="build_path", help="path used to read a compilation database."
|
||||
)
|
||||
parser.add_argument(
|
||||
"-saving-path", default="./find_all_symbols_db.yaml", help="result saving path"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
db_path = "compile_commands.json"
|
||||
|
||||
if args.build_path is not None:
|
||||
build_path = args.build_path
|
||||
else:
|
||||
build_path = find_compilation_database(db_path)
|
||||
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
|
||||
# Load the database and extract all files.
|
||||
database = json.load(open(os.path.join(build_path, db_path)))
|
||||
files = [entry["file"] for entry in database]
|
||||
|
||||
# Filter out .rc files on Windows. CMake includes them for some reason.
|
||||
files = [f for f in files if not f.endswith(".rc")]
|
||||
|
||||
max_task = args.j
|
||||
if max_task == 0:
|
||||
max_task = multiprocessing.cpu_count()
|
||||
|
||||
try:
|
||||
# Spin up a bunch of tidy-launching threads.
|
||||
queue = Queue.Queue(max_task)
|
||||
for _ in range(max_task):
|
||||
t = threading.Thread(
|
||||
target=run_find_all_symbols, args=(args, tmpdir, build_path, queue)
|
||||
)
|
||||
t.daemon = True
|
||||
t.start()
|
||||
|
||||
# Fill the queue with files.
|
||||
for name in files:
|
||||
queue.put(name)
|
||||
|
||||
# Wait for all threads to be done.
|
||||
queue.join()
|
||||
|
||||
MergeSymbols(tmpdir, args)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
# This is a sad hack. Unfortunately subprocess goes
|
||||
# bonkers with ctrl-c and we start forking merrily.
|
||||
print("\nCtrl-C detected, goodbye.")
|
||||
os.kill(0, 9)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user