Fixed stupid rtags issue
This commit is contained in:
1 parent
0beccf99e9
commit
aedfd79e02
225 files changed
+3
-41944
No files matched your search
+3
-3
@@ -109,7 +109,7 @@ RUN curl -SL $DOTNET_SDK_DOWNLOAD_URL --output dotnet.tar.gz \
|
||||
&& ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet
|
||||
|
||||
# build rtags
|
||||
copy rtags /rtags/
|
||||
run cd /rtags && cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 && make -j$(nproc) install
|
||||
run rm -rf /rtags
|
||||
copy rtags.zip /
|
||||
run unzip rtags.zip && cd /rtags && cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 && make -j$(nproc) install
|
||||
run rm -rf rtags.zip rtags
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
;;; Directory Local Variables
|
||||
;;; For more information see (info "(emacs) Directory Variables")
|
||||
|
||||
((nil
|
||||
(indent-tabs-mode))
|
||||
(c++-mode
|
||||
(c-basic-offset . 4)
|
||||
(indent-tabs-mode))
|
||||
(cmake-mode
|
||||
(cmake-tab-width . 4)
|
||||
(indent-tabs-mode))
|
||||
(python-mode
|
||||
(python-indent-offset . 4)
|
||||
(tab-width . 4)
|
||||
(indent-tabs-mode)))
|
||||
@@ -1,36 +0,0 @@
|
||||
#
|
||||
# Available repositories are listed here:
|
||||
# https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json
|
||||
#
|
||||
|
||||
language: cpp
|
||||
sudo: required
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.9
|
||||
- python3-pip
|
||||
- libcppunit-dev
|
||||
|
||||
git:
|
||||
depth: 3
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
compiler: g++
|
||||
dist: trusty
|
||||
env: T=g++-linux COMPILER_VERSION=-4.9
|
||||
- os: osx
|
||||
compiler: clang
|
||||
env: T=clang-osx
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- develop
|
||||
|
||||
script: ./scripts/travis.sh
|
||||
@@ -1,106 +0,0 @@
|
||||
cmake_minimum_required(VERSION 2.8.6)
|
||||
project(rtags)
|
||||
set(RTAGS_VERSION_MAJOR 2)
|
||||
set(RTAGS_VERSION_MINOR 18)
|
||||
set(RTAGS_VERSION_DATABASE 125)
|
||||
set(RTAGS_VERSION_SOURCES_FILE 14)
|
||||
set(RTAGS_VERSION ${RTAGS_VERSION_MAJOR}.${RTAGS_VERSION_MINOR}.${RTAGS_VERSION_DATABASE})
|
||||
|
||||
set(RCT_WITH_TESTS ${BUILD_TESTING})
|
||||
|
||||
set(CMAKE_LEGACY_CYGWIN_WIN32 0)
|
||||
|
||||
if (APPLE)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g")
|
||||
else ()
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ggdb")
|
||||
endif ()
|
||||
|
||||
if (ASAN)
|
||||
string(REPLACE "," ";" ASANS ${ASAN})
|
||||
foreach (SANITIZE ${ASANS})
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=${SANITIZE}")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=${SANITIZE}")
|
||||
endforeach ()
|
||||
endif ()
|
||||
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING
|
||||
"Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
|
||||
endif()
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
set(CMAKE_COMPILER_IS_CLANGCXX TRUE)
|
||||
endif()
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
|
||||
|
||||
include(FeatureSummary)
|
||||
add_subdirectory(src)
|
||||
|
||||
add_custom_target(man
|
||||
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/scripts/gen-man-pages.sh" "${PROJECT_BINARY_DIR}/bin"
|
||||
DEPENDS man/man7/rdm.7 man/man7/rc.7)
|
||||
add_dependencies(man rdm rc)
|
||||
|
||||
if (EXISTS "rules.ninja")
|
||||
add_custom_target(release COMMAND ${CMAKE_COMMAND} -GNinja -DCMAKE_BUILD_TYPE=Release . WORKING_DIRECTORY .)
|
||||
add_custom_target(debug COMMAND ${CMAKE_COMMAND} -GNinja -DCMAKE_BUILD_TYPE=Debug . WORKING_DIRECTORY .)
|
||||
else()
|
||||
add_custom_target(release COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Release . WORKING_DIRECTORY .)
|
||||
add_custom_target(debug COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug . WORKING_DIRECTORY .)
|
||||
endif()
|
||||
|
||||
# Create uninstall script and target
|
||||
configure_file("${CMAKE_MODULE_PATH}/Uninstall.cmake.in" "${CMAKE_BINARY_DIR}/Uninstall.cmake" IMMEDIATE @ONLY)
|
||||
add_custom_target(uninstall ${CMAKE_COMMAND} -P "${CMAKE_BINARY_DIR}/Uninstall.cmake")
|
||||
|
||||
if (RTAGS_ENABLE_DEV_OPTIONS)
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "RTAGS")
|
||||
set(CPACK_PACKAGE_VENDOR "RTags")
|
||||
set(CPACK_PACKAGE_CONTACT "http://rtags.net")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR ${RTAGS_VERSION_MAJOR})
|
||||
set(CPACK_PACKAGE_VERSION_MINOR ${RTAGS_VERSION_MINOR})
|
||||
set(CPACK_PACKAGE_VERSION_DATABASE ${RTAGS_VERSION_DATABASE})
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt")
|
||||
set(CPACK_PACKAGE_VERSION "${RTAGS_VERSION}")
|
||||
|
||||
if (NOT CPACK_GENERATOR)
|
||||
set(CPACK_GENERATOR TGZ)
|
||||
endif ()
|
||||
set(CPACK_SOURCE_PACKAGE_FILE_NAME "rtags-${RTAGS_VERSION_MAJOR}.${RTAGS_VERSION_MINOR}"
|
||||
CACHE INTERNAL "tarball basename")
|
||||
set(CPACK_SOURCE_GENERATOR ${CPACK_GENERATOR})
|
||||
|
||||
set(CPACK_SOURCE_IGNORE_FILES
|
||||
"~$"
|
||||
"\\\\.gitignore$"
|
||||
"\\\\.gitmodules$"
|
||||
"^${PROJECT_SOURCE_DIR}/.git/"
|
||||
"^${PROJECT_SOURCE_DIR}/build/"
|
||||
)
|
||||
|
||||
include(CPack)
|
||||
endif ()
|
||||
|
||||
include(CTest)
|
||||
|
||||
if (BUILD_TESTING)
|
||||
find_package(Perl QUIET)
|
||||
if(PERL_FOUND)
|
||||
add_test(NAME SBRootTest COMMAND perl "${CMAKE_SOURCE_DIR}/tests/sbroot/sbroot_test.pl")
|
||||
set_property(TEST SBRootTest PROPERTY ENVIRONMENT "RTAGS_BINARY_DIR=${CMAKE_BINARY_DIR}/bin")
|
||||
else()
|
||||
message(WARNING "Perl not found! Excluding sbroot test from tests.")
|
||||
endif()
|
||||
|
||||
find_program(NOSETEST NAMES nosetests nosetests-2.7 PATHS "$ENV{HOME}/.local/bin")
|
||||
if (NOSETEST)
|
||||
add_test(NAME nosetests COMMAND ${NOSETEST} -w ${CMAKE_SOURCE_DIR}/tests/automated --no-byte-compile -v)
|
||||
set_property(TEST nosetests PROPERTY ENVIRONMENT "RTAGS_BINARY_DIR=${CMAKE_BINARY_DIR}/bin")
|
||||
else()
|
||||
message(WARNING "Nosetests executable not found! Excluding automated_tests from tests.")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
feature_summary(INCLUDE_QUIET_PACKAGES WHAT ALL)
|
||||
@@ -1,14 +0,0 @@
|
||||
Copyright (C) 2011-2016 Jan Erik Hanssen and Anders Bakken
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-1220
File diff suppressed because it is too large.
Load diff
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
[ -z "$RTAGS_DISABLED" ] && [ -x "$(which rc)" ] && rc --silent --compile "$@" &
|
||||
[ "$RTAGS_RMAKE" ] && exit 1
|
||||
compiler="$1"
|
||||
shift
|
||||
$compiler "$@"
|
||||
exit $?
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -z "$RTAGS_DISABLED" ] && [ -x "$(which rc)" ]; then
|
||||
rc --silent --compile "$ICECC_CXX" "$@" &
|
||||
fi
|
||||
|
||||
[ "$RTAGS_RMAKE" ] && exit 1
|
||||
exit 0
|
||||
@@ -1,85 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ "$RTAGS_GCC_WRAPPER" = "1" ]; then
|
||||
echo 1>&2 "Recursive invocation of gcc-rtags-wrapper.sh detected"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# tup support
|
||||
|
||||
function eliminateTupVariant
|
||||
{
|
||||
topDir="$PWD"
|
||||
while [ "$PWD" != / -a ! -e ../.tup -a ! -e .tup ]; do
|
||||
cd ..
|
||||
done
|
||||
|
||||
if [ -e tup.config ]; then
|
||||
configDir=$PWD
|
||||
cd ..
|
||||
echo "$PWD${topDir#$configDir}"
|
||||
else
|
||||
echo "$topDir"
|
||||
fi
|
||||
}
|
||||
|
||||
pushd . >/dev/null
|
||||
if pwd | grep --quiet /.tup/mnt/@tupjob-; then
|
||||
# get out from the tup virtual filesystem
|
||||
cd "/${PWD#*/.tup/mnt/@tupjob-*/}"
|
||||
|
||||
# get out from the tup variant directory (if any)
|
||||
cd "$(eliminateTupVariant)"
|
||||
fi
|
||||
|
||||
# tup support end
|
||||
|
||||
rc=$(which rc)
|
||||
for i in $(which -a "$(basename "$0")"); do
|
||||
filename="$i"
|
||||
max=10
|
||||
while [ $max -gt 0 -a -L "$filename" ]; do
|
||||
max=$((max - 1))
|
||||
link=$(readlink "$filename")
|
||||
if echo "$link" | grep --quiet "^/"; then
|
||||
filename="$link"
|
||||
else
|
||||
filename="$(dirname $filename)/$link"
|
||||
fi
|
||||
done
|
||||
|
||||
filename=$(basename "$filename")
|
||||
if [ "$filename" != "gcc-rtags-wrapper.sh" ] && [ -z "$PLAST" -o "$filename" != "plastc" ]; then
|
||||
[ -n "$RTAGS_SERVER_FILE" ] && RTAGS_ARGS="$RTAGS_ARGS -n$RTAGS_SERVER_FILE"
|
||||
[ -n "$RTAGS_PROJECT" ] && RTAGS_ARGS="$RTAGS_ARGS --project-root=$RTAGS_PROJECT"
|
||||
[ -z "$RTAGS_COMPILE_TIMEOUT" ] && RTAGS_COMPILE_TIMEOUT=3000
|
||||
|
||||
if [ -z "$RTAGS_DISABLED" ] && [ -x "$rc" ]; then
|
||||
case $(basename "$i") in
|
||||
mpicc)
|
||||
cc=cc
|
||||
flags="$(mpicc -show|cut -d ' ' -f 2-) -DMPICH_NO_ATTR_TYPE_TAGS"
|
||||
;;
|
||||
mpicxx)
|
||||
cc=c++
|
||||
flags="$(mpicxx -show|cut -d ' ' -f 2-) -DMPICH_NO_ATTR_TYPE_TAGS"
|
||||
;;
|
||||
*)
|
||||
cc="$i"
|
||||
flags=""
|
||||
;;
|
||||
esac
|
||||
$rc --timeout=$RTAGS_COMPILE_TIMEOUT $RTAGS_ARGS --silent --compile "$cc" $flags "$@" &
|
||||
disown &>/dev/null # rc might be finished by now and if so disown will yell at us
|
||||
fi
|
||||
[ "$RTAGS_RMAKE" ] && exit 0
|
||||
|
||||
export RTAGS_GCC_WRAPPER=1
|
||||
popd >/dev/null
|
||||
"$i" "$@"
|
||||
exit $?
|
||||
else
|
||||
PATH="$(echo "$PATH" | sed -e "s,$(dirname "$i")/*:*,,g")"
|
||||
fi
|
||||
done
|
||||
exit 1 ### no compiler found?
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
[ -z "$RTAGS_DISABLED" ] && [ -x "$(which rc)" ] && rc --silent --compile "$@" &
|
||||
[ -n "$RTAGS_RMAKE" ] && exit 0
|
||||
compiler="$1"
|
||||
shift
|
||||
$compiler "$@"
|
||||
exit $?
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
1
|
||||
Memcheck:Cond
|
||||
fun:_ZNK5clang4Expr21refersToVectorElementEv
|
||||
obj:*
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
2
|
||||
Memcheck:Cond
|
||||
fun:_ZNK5clang4Expr21refersToVectorElementEv
|
||||
}
|
||||
{
|
||||
3
|
||||
Memcheck:Cond
|
||||
fun:_ZNK5clang4Expr17isTemporaryObjectERNS_10ASTContextEPKNS_13CXXRecordDeclE
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
set(FORCE_BASH_COMPLETION_INSTALLATION FALSE CACHE BOOL "Force bash completion installation")
|
||||
|
||||
if(FORCE_BASH_COMPLETION_INSTALLATION AND "${BASH_COMPLETION_COMPLETIONSDIR}" STREQUAL "")
|
||||
set(BASH_COMPLETION_COMPLETIONSDIR "/share/bash-completion/completions")
|
||||
endif()
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
set_package_properties(PkgConfig
|
||||
PROPERTIES
|
||||
URL "https://www.freedesktop.org/wiki/Software/pkg-config/"
|
||||
DESCRIPTION "helper tool"
|
||||
TYPE OPTIONAL
|
||||
PURPOSE "We use it to get the bash completion installation path, and replace the prefix with the value of CMAKE_INSTALL_PREFIX.")
|
||||
if(PKG_CONFIG_FOUND OR FORCE_BASH_COMPLETION_INSTALLATION)
|
||||
if(PKG_CONFIG_FOUND AND NOT FORCE_BASH_COMPLETION_INSTALLATION)
|
||||
execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=completionsdir bash-completion
|
||||
RESULT_VARIABLE BASH_COMPLETION_UNAVAILABLE
|
||||
OUTPUT_VARIABLE BASH_COMPLETION_COMPLETIONSDIR
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT BASH_COMPLETION_UNAVAILABLE)
|
||||
execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=prefix bash-completion
|
||||
OUTPUT_VARIABLE BASH_COMPLETION_COMPLETIONSDIR_PREFIX
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
string(REPLACE "${BASH_COMPLETION_COMPLETIONSDIR_PREFIX}" "" BASH_COMPLETION_COMPLETIONSDIR ${BASH_COMPLETION_COMPLETIONSDIR})
|
||||
endif()
|
||||
endif()
|
||||
if(NOT BASH_COMPLETION_UNAVAILABLE OR FORCE_BASH_COMPLETION_INSTALLATION)
|
||||
set(BASH_COMPLETION_COMPLETIONSDIR "${CMAKE_INSTALL_PREFIX}${BASH_COMPLETION_COMPLETIONSDIR}" CACHE PATH "Bash completion installation directory" FORCE)
|
||||
mark_as_advanced(BASH_COMPLETION_COMPLETIONSDIR)
|
||||
set(BASH_COMPLETION_FOUND TRUE)
|
||||
endif()
|
||||
endif()
|
||||
@@ -1,23 +0,0 @@
|
||||
macro(ClangVersion path version_major version_minor)
|
||||
if(EXISTS "${path}/clang/Basic/Version.inc")
|
||||
file(READ "${path}/clang/Basic/Version.inc" CLANG_VERSION_DATA)
|
||||
string(REGEX REPLACE ";" "\\\\;" CLANG_VERSION_DATA ${CLANG_VERSION_DATA})
|
||||
string(REGEX REPLACE "\n" ";" CLANG_VERSION_DATA ${CLANG_VERSION_DATA})
|
||||
foreach(line ${CLANG_VERSION_DATA})
|
||||
string(REGEX REPLACE "^#define CLANG_VERSION ([0-9]+\\.[0-9]+)$" "\\1" CLANG_VERSION_STRING ${line})
|
||||
if(DEFINED CLANG_VERSION_STRING)
|
||||
string(REGEX REPLACE "^([0-9]+)\\.[0-9]+$" "\\1" CLANG_VERSION_MAJOR ${CLANG_VERSION_STRING})
|
||||
string(REGEX REPLACE "^[0-9]+\\.([0-9]+)$" "\\1" CLANG_VERSION_MINOR ${CLANG_VERSION_STRING})
|
||||
if(NOT ${CLANG_VERSION_MAJOR} STREQUAL "" AND NOT ${CLANG_VERSION_MINOR} STREQUAL "")
|
||||
break()
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
if(NOT ${CLANG_VERSION_MAJOR} STREQUAL "" AND NOT ${CLANG_VERSION_MINOR} STREQUAL "")
|
||||
set(${version_major} ${CLANG_VERSION_MAJOR})
|
||||
set(${version_minor} ${CLANG_VERSION_MINOR})
|
||||
else()
|
||||
message(FATAL_ERROR "Unable to parse ClangVersion from ${path}/clang/Basic/Version.inc")
|
||||
endif()
|
||||
endmacro()
|
||||
@@ -1,46 +0,0 @@
|
||||
#### EnsureLibraries
|
||||
|
||||
# Macro for copying needed libraries to target directory
|
||||
MACRO(EnsureLibraries EXE_NAME LIBRARY_NAMES )
|
||||
|
||||
FOREACH ( LIBRARY_NAME IN ITEMS ${LIBRARY_NAMES} )
|
||||
GET_TARGET_PROPERTY ( libLocation "${LIBRARY_NAME}" LOCATION )
|
||||
|
||||
IF ( NOT "${libLocation}" STREQUAL "libLocation-NOTFOUND" )
|
||||
|
||||
GET_TARGET_PROPERTY ( exeLocation "${EXE_NAME}" LOCATION )
|
||||
|
||||
IF ( CMAKE_COMPILER_IS_GNUCC )
|
||||
ADD_CUSTOM_COMMAND ( TARGET ${EXE_NAME}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -D LIBRARY="${libLocation}" -D TARGET="${exeLocation}" -P "${CMAKE_SOURCE_DIR}/cmake/EnsureLibrariesRunner.cmake"
|
||||
COMMENT "Copy lib ${LIBRARY_NAME} to ${EXE_NAME} build ..."
|
||||
DEPENDS ${LIBRARY_NAME}
|
||||
)
|
||||
ELSE ( CMAKE_COMPILER_IS_GNUCC )
|
||||
|
||||
INCLUDE(GenerateExportHeader)
|
||||
|
||||
IF ( NOT CYGWIN )
|
||||
GENERATE_EXPORT_HEADER( ${LIBRARY_NAME}
|
||||
BASE_NAME ${LIBRARY_NAME}
|
||||
EXPORT_MACRO_NAME ${LIBRARY_NAME}_EXPORT
|
||||
EXPORT_FILE_NAME ${LIBRARY_NAME}_Export.h
|
||||
STATIC_DEFINE ${LIBRARY_NAME}_BUILT_AS_STATIC
|
||||
)
|
||||
ENDIF ( NOT CYGWIN )
|
||||
|
||||
ADD_CUSTOM_COMMAND ( TARGET ${EXE_NAME}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${libLocation}" "$<TARGET_FILE_DIR:${EXE_NAME}>"
|
||||
COMMENT "Copy lib ${LIBRARY_NAME} to ${EXE_NAME} build ..."
|
||||
DEPENDS ${LIBRARY_NAME}
|
||||
DEPENDS osswin
|
||||
)
|
||||
ENDIF ( CMAKE_COMPILER_IS_GNUCC )
|
||||
|
||||
ENDIF ( NOT "${libLocation}" STREQUAL "libLocation-NOTFOUND" )
|
||||
|
||||
ENDFOREACH ( LIBRARY_NAME IN ITEMS ${LIBRARY_NAMES} )
|
||||
|
||||
ENDMACRO(EnsureLibraries)
|
||||
@@ -1,28 +0,0 @@
|
||||
|
||||
IF ( NOT EXISTS ${LIBRARY} )
|
||||
MESSAGE ( FATAL_ERROR "The provided file ${LIBRARY} does not exist" )
|
||||
RETURN ()
|
||||
ENDIF ( NOT EXISTS ${LIBRARY} )
|
||||
|
||||
SET ( TARGET_PATH "${TARGET}" )
|
||||
|
||||
IF ( EXISTS ${TARGET} AND NOT IS_DIRECTORY ${TARGET} )
|
||||
FILE ( TO_NATIVE_PATH "${TARGET}" TARGET_PATH )
|
||||
IF ( EXISTS ${TARGET_PATH} )
|
||||
GET_FILENAME_COMPONENT ( TARGET_PATH ${TARGET} PATH )
|
||||
ENDIF ( EXISTS ${TARGET_PATH} )
|
||||
ENDIF ( EXISTS ${TARGET} AND NOT IS_DIRECTORY ${TARGET} )
|
||||
|
||||
IF ( NOT EXISTS ${TARGET_PATH} AND NOT IS_DIRECTORY ${TARGET_PATH} )
|
||||
MESSAGE ( FATAL_ERROR "The provided target ${TARGET_PATH} does not exist, and is not a directory" )
|
||||
RETURN ()
|
||||
ENDIF ( NOT EXISTS ${TARGET_PATH} AND NOT IS_DIRECTORY ${TARGET_PATH} )
|
||||
SET ( OUTPUT_TARGET ${TARGET_PATH} )
|
||||
|
||||
FILE ( TO_NATIVE_PATH "${LIBRARY}" NATIVE_LIBRARY_PATH )
|
||||
FILE ( TO_NATIVE_PATH "${OUTPUT_TARGET}/" NATIVE_TARGET_PATH )
|
||||
|
||||
#MESSAGE ( STATUS "Copying ${NATIVE_LIBRARY_PATH} => ${NATIVE_TARGET_PATH}" )
|
||||
|
||||
FILE ( COPY ${NATIVE_LIBRARY_PATH} DESTINATION ${NATIVE_TARGET_PATH} )
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
# FindLibClang
|
||||
#
|
||||
# This module searches libclang and llvm-config, the llvm-config tool is used to
|
||||
# get information about the installed llvm/clang package to compile LLVM based
|
||||
# programs.
|
||||
#
|
||||
# It defines the following variables
|
||||
#
|
||||
# ``LIBCLANG_LLVM_CONFIG_EXECUTABLE``
|
||||
# the llvm-config tool to get various information.
|
||||
# ``LIBCLANG_LIBRARIES``
|
||||
# the clang libraries to link against to use Clang/LLVM.
|
||||
# ``LIBCLANG_LIBDIR``
|
||||
# the directory where the clang libraries are located.
|
||||
# ``LIBCLANG_FOUND``
|
||||
# true if libclang was found
|
||||
# ``LIBCLANG_VERSION_STRING``
|
||||
# version number as a string
|
||||
# ``LIBCLANG_CXXFLAGS``
|
||||
# the compiler flags for files that include LLVM headers
|
||||
#
|
||||
#=============================================================================
|
||||
# Copyright (C) 2011, 2012, 2013 Jan Erik Hanssen and Anders Bakken
|
||||
# Copyright (C) 2015 Christian Schwarzgruber <c.schwarzgruber.cs@gmail.com>
|
||||
#
|
||||
# This file is part of RTags (http://rtags.net).
|
||||
#
|
||||
# RTags is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# RTags is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with RTags. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
if (NOT LIBCLANG_ROOT_DIR)
|
||||
set(LIBCLANG_ROOT_DIR $ENV{LIBCLANG_ROOT_DIR})
|
||||
endif ()
|
||||
|
||||
if (NOT LIBCLANG_LLVM_CONFIG_EXECUTABLE)
|
||||
set(LIBCLANG_LLVM_CONFIG_EXECUTABLE $ENV{LIBCLANG_LLVM_CONFIG_EXECUTABLE})
|
||||
if (NOT LIBCLANG_LLVM_CONFIG_EXECUTABLE)
|
||||
if (APPLE)
|
||||
foreach(major RANGE 9 3)
|
||||
foreach(minor RANGE 9 0)
|
||||
foreach(patch RANGE 9 0)
|
||||
message(STATUS "trying llvm-config llvm-config${major}${minor} in /usr/local/Cellar/llvm/${major}.${minor}.${patch}/bin")
|
||||
find_program(LIBCLANG_LLVM_CONFIG_EXECUTABLE NAMES llvm-config llvm-config${major}${minor} llvm-config-${major}${minor} llvm-config-${major} llvm-config${major} PATHS /usr/local/Cellar/llvm/${major}.${minor}.${patch}/bin)
|
||||
if (LIBCLANG_LLVM_CONFIG_EXECUTABLE)
|
||||
break()
|
||||
endif ()
|
||||
endforeach ()
|
||||
if (LIBCLANG_LLVM_CONFIG_EXECUTABLE)
|
||||
break()
|
||||
endif ()
|
||||
endforeach ()
|
||||
if (LIBCLANG_LLVM_CONFIG_EXECUTABLE)
|
||||
break()
|
||||
endif ()
|
||||
endforeach ()
|
||||
else ()
|
||||
set(llvm_config_names llvm-config)
|
||||
foreach(major RANGE 9 3)
|
||||
list(APPEND llvm_config_names "llvm-config${major}" "llvm-config-${major}")
|
||||
foreach(minor RANGE 9 0)
|
||||
list(APPEND llvm_config_names "llvm-config${major}${minor}" "llvm-config-${major}.${minor}" "llvm-config-mp-${major}.${minor}")
|
||||
endforeach ()
|
||||
endforeach ()
|
||||
find_program(LIBCLANG_LLVM_CONFIG_EXECUTABLE NAMES ${llvm_config_names})
|
||||
endif ()
|
||||
endif ()
|
||||
if (LIBCLANG_LLVM_CONFIG_EXECUTABLE)
|
||||
message(STATUS "llvm-config executable found: ${LIBCLANG_LLVM_CONFIG_EXECUTABLE}")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (NOT LIBCLANG_CXXFLAGS)
|
||||
if (NOT LIBCLANG_LLVM_CONFIG_EXECUTABLE)
|
||||
message(FATAL_ERROR "Could NOT find llvm-config executable and LIBCLANG_CXXFLAGS is not set ")
|
||||
endif ()
|
||||
execute_process(COMMAND ${LIBCLANG_LLVM_CONFIG_EXECUTABLE} --cxxflags OUTPUT_VARIABLE LIBCLANG_CXXFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if (NOT LIBCLANG_CXXFLAGS)
|
||||
find_path(LIBCLANG_CXXFLAGS_HACK_CMAKECACHE_DOT_TEXT_BULLSHIT clang-c/Index.h HINTS ${LIBCLANG_ROOT_DIR}/include NO_DEFAULT_PATH)
|
||||
if (NOT EXISTS ${LIBCLANG_CXXFLAGS_HACK_CMAKECACHE_DOT_TEXT_BULLSHIT})
|
||||
find_path(LIBCLANG_CXXFLAGS clang-c/Index.h)
|
||||
if (NOT EXISTS ${LIBCLANG_CXXFLAGS})
|
||||
message(FATAL_ERROR "Could NOT find clang include path. You can fix this by setting LIBCLANG_CXXFLAGS in your shell or as a cmake variable.")
|
||||
endif ()
|
||||
else ()
|
||||
set(LIBCLANG_CXXFLAGS ${LIBCLANG_CXXFLAGS_HACK_CMAKECACHE_DOT_TEXT_BULLSHIT})
|
||||
endif ()
|
||||
set(LIBCLANG_CXXFLAGS "-I${LIBCLANG_CXXFLAGS}")
|
||||
endif ()
|
||||
string(REGEX MATCHALL "-(D__?[a-zA-Z_]*|I([^\" ]+|\"[^\"]+\"))" LIBCLANG_CXXFLAGS "${LIBCLANG_CXXFLAGS}")
|
||||
string(REGEX REPLACE ";" " " LIBCLANG_CXXFLAGS "${LIBCLANG_CXXFLAGS}")
|
||||
set(LIBCLANG_CXXFLAGS ${LIBCLANG_CXXFLAGS} CACHE STRING "The LLVM C++ compiler flags needed to compile LLVM based applications.")
|
||||
unset(LIBCLANG_CXXFLAGS_HACK_CMAKECACHE_DOT_TEXT_BULLSHIT CACHE)
|
||||
endif ()
|
||||
|
||||
if (NOT EXISTS ${LIBCLANG_LIBDIR})
|
||||
if (NOT LIBCLANG_LLVM_CONFIG_EXECUTABLE)
|
||||
message(FATAL_ERROR "Could NOT find llvm-config executable and LIBCLANG_LIBDIR is not set ")
|
||||
endif ()
|
||||
execute_process(COMMAND ${LIBCLANG_LLVM_CONFIG_EXECUTABLE} --libdir OUTPUT_VARIABLE LIBCLANG_LIBDIR OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if (NOT EXISTS ${LIBCLANG_LIBDIR})
|
||||
message(FATAL_ERROR "Could NOT find clang libdir. You can fix this by setting LIBCLANG_LIBDIR in your shell or as a cmake variable.")
|
||||
endif ()
|
||||
set(LIBCLANG_LIBDIR ${LIBCLANG_LIBDIR} CACHE STRING "Path to the clang library.")
|
||||
endif ()
|
||||
|
||||
if (NOT LIBCLANG_LIBRARIES)
|
||||
find_library(LIBCLANG_LIB_HACK_CMAKECACHE_DOT_TEXT_BULLSHIT NAMES clang libclang HINTS ${LIBCLANG_LIBDIR} ${LIBCLANG_ROOT_DIR}/lib NO_DEFAULT_PATH)
|
||||
if (LIBCLANG_LIB_HACK_CMAKECACHE_DOT_TEXT_BULLSHIT)
|
||||
set(LIBCLANG_LIBRARIES "${LIBCLANG_LIB_HACK_CMAKECACHE_DOT_TEXT_BULLSHIT}")
|
||||
else ()
|
||||
find_library(LIBCLANG_LIBRARIES NAMES clang libclang)
|
||||
if (NOT EXISTS ${LIBCLANG_LIBRARIES})
|
||||
set (LIBCLANG_LIBRARIES "-L${LIBCLANG_LIBDIR}" "-lclang" "-Wl,-rpath,${LIBCLANG_LIBDIR}")
|
||||
endif ()
|
||||
endif ()
|
||||
unset(LIBCLANG_LIB_HACK_CMAKECACHE_DOT_TEXT_BULLSHIT CACHE)
|
||||
endif ()
|
||||
|
||||
set(LIBCLANG_LIBRARY ${LIBCLANG_LIBRARIES} CACHE FILEPATH "Path to the libclang library")
|
||||
|
||||
if (LIBCLANG_LLVM_CONFIG_EXECUTABLE)
|
||||
execute_process(COMMAND ${LIBCLANG_LLVM_CONFIG_EXECUTABLE} --version OUTPUT_VARIABLE LIBCLANG_VERSION_STRING OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
else ()
|
||||
set(LIBCLANG_VERSION_STRING "Unknown")
|
||||
endif ()
|
||||
message("-- Using Clang version ${LIBCLANG_VERSION_STRING} from ${LIBCLANG_LIBDIR} with CXXFLAGS ${LIBCLANG_CXXFLAGS}")
|
||||
|
||||
# Handly the QUIETLY and REQUIRED arguments and set LIBCLANG_FOUND to TRUE if all listed variables are TRUE
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(LibClang DEFAULT_MSG LIBCLANG_LIBRARY LIBCLANG_CXXFLAGS LIBCLANG_LIBDIR)
|
||||
mark_as_advanced(LIBCLANG_CXXFLAGS LIBCLANG_LIBRARY LIBCLANG_LLVM_CONFIG_EXECUTABLE LIBCLANG_LIBDIR)
|
||||
@@ -1,70 +0,0 @@
|
||||
find_package(Git)
|
||||
if(GIT_FOUND)
|
||||
set(USE_GIT 1)
|
||||
else()
|
||||
set(USE_GIT 0)
|
||||
endif()
|
||||
|
||||
if(${USE_GIT} EQUAL 1)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} describe --tags --dirty
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_DESCRIBE_VERSION
|
||||
RESULT_VARIABLE GIT_DESCRIBE_RESULT
|
||||
ERROR_VARIABLE GIT_DESCRIBE_ERROR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} config --get user.name
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_CONFIG_USERNAME
|
||||
RESULT_VARIABLE GIT_CONFIG_RESULT
|
||||
ERROR_VARIABLE GIT_CONFIG_ERROR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
else()
|
||||
set(GIT_DESCRIBE_RESULT -1)
|
||||
set(GIT_CONFIG_RESULT -1)
|
||||
endif()
|
||||
|
||||
#
|
||||
# Deal with version first
|
||||
#
|
||||
if(GIT_DESCRIBE_RESULT EQUAL 0)
|
||||
file(WRITE ${CMAKE_BINARY_DIR}/source/VERSION "${GIT_DESCRIBE_VERSION}")
|
||||
else()
|
||||
file(READ ${CMAKE_SOURCE_DIR}/VERSION GIT_DESCRIBE_VERSION)
|
||||
endif()
|
||||
|
||||
string(REGEX REPLACE "v([0-9]*)\\.([0-9]*)(-(.*))?"
|
||||
"\\1;\\2;\\4" VLIST "${GIT_DESCRIBE_VERSION}")
|
||||
#message(INFO " vlist ${VLIST}")
|
||||
|
||||
list(GET VLIST 0 VERSION_MAJOR)
|
||||
list(GET VLIST 1 VERSION_MINOR)
|
||||
list(LENGTH VLIST VLISTLEN)
|
||||
if(VLISTLEN EQUAL 3)
|
||||
list(GET VLIST 2 VERSION_EXTRA)
|
||||
endif()
|
||||
set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}-${VERSION_EXTRA}")
|
||||
|
||||
#
|
||||
# Deal with username
|
||||
#
|
||||
string(LENGTH "${GIT_CONFIG_USERNAME}" GIT_CONFIG_USERNAME_LENGTH)
|
||||
#message(INFO "uname <${GIT_CONFIG_USERNAME}>")
|
||||
#message(INFO "length ${GIT_CONFIG_USERNAME_LENGTH}")
|
||||
if(GIT_CONFIG_USERNAME_LENGTH EQUAL 0)
|
||||
set(GIT_CONFIG_RESULT -1)
|
||||
endif()
|
||||
|
||||
if(GIT_CONFIG_RESULT EQUAL -1)
|
||||
if(WIN32)
|
||||
set(PKG_USERNAME "$ENV{USERNAME}")
|
||||
else()
|
||||
set(PKG_USERNAME "$ENV{USER}")
|
||||
endif()
|
||||
else()
|
||||
set(PKG_USERNAME "${GIT_CONFIG_USERNAME}")
|
||||
endif()
|
||||
#message(INFO "user ${PKG_USERNAME}")
|
||||
@@ -1,39 +0,0 @@
|
||||
set(MANIFEST "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt")
|
||||
|
||||
if(NOT EXISTS ${MANIFEST})
|
||||
message(FATAL_ERROR "Cannot find install manifest: '${MANIFEST}'")
|
||||
endif()
|
||||
|
||||
file(STRINGS ${MANIFEST} files)
|
||||
foreach(file ${files})
|
||||
if(IS_SYMLINK ${file} OR EXISTS ${file})
|
||||
message(STATUS "Removing file: '${file}'.")
|
||||
exec_program(${CMAKE_COMMAND} ARGS "-E remove ${file}" OUTPUT_VARIABLE stdout RETURN_VALUE result)
|
||||
if(NOT "${result}" STREQUAL 0)
|
||||
message(FATAL_ERROR "Failed to remove file: '${file}'.")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "File '${file}' does not exist.")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# We will not traverse back and delete "arbitrary" folders.
|
||||
# An exception are directories which are rtags specific directories like,
|
||||
# "rtags" under "INSTALL_PREFIX/usr/emacs/site-lisp/".
|
||||
set(directories "@CMAKE_INSTALL_PREFIX@/share/emacs/site-lisp/rtags")
|
||||
foreach(dir ${directories})
|
||||
if(IS_DIRECTORY ${dir} AND EXISTS ${dir})
|
||||
file(GLOB files "${dir}/*")
|
||||
if(NOT files)
|
||||
message(STATUS "Removing directory: '${dir}'.")
|
||||
exec_program(${CMAKE_COMMAND} ARGS "-E remove_directory ${dir}" OUTPUT_VARIABLE stdout RETURN_VALUE result)
|
||||
if(NOT "${result}" STREQUAL 0)
|
||||
message(FATAL_ERROR "Failed to remove directory: '${dir}'.")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Directory '${dir}' not empty.")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Directory '${dir}' does not exist.")
|
||||
endif()
|
||||
endforeach()
|
||||
File diff suppressed because it is too large.
Load diff
Vendored
-132
@@ -1,132 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
function usage ()
|
||||
{
|
||||
echo "configure [OPTIONS...]"
|
||||
echo " --help|-h Display this help"
|
||||
echo " --cmake [arg] Use arg instead of \"cmake\""
|
||||
echo " --cmake-argument [arg] Pass additional arguments to cmake"
|
||||
echo " --cmake-generator [arg] Use this generator for cmake (e.g. Ninja)"
|
||||
echo " --enable-dev-options Set certain development options (cpack)"
|
||||
echo " --asan Enable -fsanitize=address and -fsanitize=undefined"
|
||||
echo " --debug Compile RTags in debug mode"
|
||||
echo " --release Compile RTags in release mode (default)"
|
||||
echo " --system-clang Don't build llvm/clang as part of RTags' build process (default)"
|
||||
echo " --build-tests Build tests"
|
||||
echo " --build-clang Download and build llvm/clang as part of RTags' build process"
|
||||
echo " --no-install Generate a build that isn't going to be installed"
|
||||
echo " --prefix [arg] Set install prefix to arg"
|
||||
echo " --clang-libraries [arg] Override what libraries RTags will use to link against clang/llvm"
|
||||
echo " --clang-cxxflags [arg] Override what cxxflags RTags will use for compilation to be able to include clang headers."
|
||||
echo " --clang-libdir [arg] Sets the libdir RTags will use for clang."
|
||||
echo " --cotire Enable cotire when building RTags"
|
||||
echo " --emacs Use arg instead of \"emacs\" when byte-compiling elisp"
|
||||
echo " --lua-enabled Enable LUA AST scripting"
|
||||
echo " --no-lua-files-install Don't install RTags lua files"
|
||||
echo " --no-elisp-files-install Don't install RTags elisp files"
|
||||
echo " --no-elisp-bytecompile Don't bytecompile RTags elisp files"
|
||||
echo " --elisp-install-location [arg] Install elisp files to this location"
|
||||
}
|
||||
|
||||
CMAKE=cmake
|
||||
CMAKE_ARGS="-DCMAKE_EXPORT_COMPILE_COMMANDS=1"
|
||||
BUILD=
|
||||
while [ -n "$1" ]; do
|
||||
case "$1" in
|
||||
--help|-h)
|
||||
usage > /dev/stdout
|
||||
exit 0
|
||||
;;
|
||||
--cmake)
|
||||
shift
|
||||
CMAKE="$1"
|
||||
;;
|
||||
--cmake-argument)
|
||||
shift
|
||||
CMAKE_ARGS="${CMAKE_ARGS} \"$1\""
|
||||
;;
|
||||
--cmake-generator)
|
||||
shift
|
||||
CMAKE_GENERATOR="$1"
|
||||
;;
|
||||
--enable-dev-options)
|
||||
CMAKE_ARGS="${CMAKE_ARGS} -DRTAGS_ENABLE_DEV_OPTIONS=1"
|
||||
;;
|
||||
--debug)
|
||||
CMAKE_ARGS="${CMAKE_ARGS} -DCMAKE_BUILD_TYPE=Debug"
|
||||
;;
|
||||
--release)
|
||||
CMAKE_ARGS="${CMAKE_ARGS} -DCMAKE_BUILD_TYPE=Release"
|
||||
;;
|
||||
--system-clang)
|
||||
BUILD=
|
||||
;;
|
||||
--build-clang)
|
||||
BUILD=1
|
||||
;;
|
||||
--without-tests)
|
||||
CMAKE_ARGS="${CMAKE_ARGS} -DBUILD_TESTING=0"
|
||||
;;
|
||||
--no-install)
|
||||
CMAKE_ARGS="${CMAKE_ARGS} -DRTAGS_NO_INSTALL=1"
|
||||
;;
|
||||
--prefix)
|
||||
shift
|
||||
CMAKE_ARGS="${CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX=\"$1\""
|
||||
;;
|
||||
--lua-enabled)
|
||||
CMAKE_ARGS="${CMAKE_ARGS} -DLUA_ENABLED=1"
|
||||
;;
|
||||
--clang-libraries)
|
||||
shift
|
||||
CMAKE_ARGS="${CMAKE_ARGS} -DLIBCLANG_LIBRARIES=\"$1\""
|
||||
;;
|
||||
--clang-cxxflags)
|
||||
shift
|
||||
CMAKE_ARGS="${CMAKE_ARGS} -DLIBCLANG_CXXFLAGS=\"$1\""
|
||||
;;
|
||||
--clang-libdir)
|
||||
shift
|
||||
CMAKE_ARGS="${CMAKE_ARGS} -DLIBCLANG_LIBDIR=\"$1\""
|
||||
;;
|
||||
--cotire)
|
||||
CMAKE_ARGS="${CMAKE_ARGS} -DRTAGS_COTIRE=1"
|
||||
;;
|
||||
--emacs)
|
||||
shift
|
||||
CMAKE_ARGS="${CMAKE_ARGS} -DEMACS=\"$1\""
|
||||
;;
|
||||
--no-lua-files-install)
|
||||
CMAKE_ARGS="${CMAKE_ARGS} -DRTAGS_NO_LUA_FILES=1"
|
||||
;;
|
||||
--no-elisp-files-install)
|
||||
CMAKE_ARGS="${CMAKE_ARGS} -DRTAGS_NO_ELISP_FILES=1"
|
||||
;;
|
||||
--no-elisp-bytecompile)
|
||||
CMAKE_ARGS="${CMAKE_ARGS} -DRTAGS_NO_ELISP_BYTECOMPILE=1"
|
||||
;;
|
||||
--asan)
|
||||
CMAKE_ARGS="${CMAKE_ARGS} -DASAN=address,undefined"
|
||||
;;
|
||||
--asan=*)
|
||||
CMAKE_ARGS="${CMAKE_ARGS} -DASAN=`echo $1 | sed -e 's,--asan=,,'`"
|
||||
;;
|
||||
--elisp-install-location)
|
||||
shift
|
||||
CMAKE_ARGS="${CMAKE_ARGS} -DRTAGS_ELISP_INSTALL_LOCATION=\"$1\""
|
||||
;;
|
||||
*)
|
||||
usage > /dev/stderr
|
||||
echo "Unknown option $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
[ -n "$CMAKE_GENERATOR" ] && CMAKE_ARGS="${CMAKE_ARGS} -G \"$CMAKE_GENERATOR\""
|
||||
[ -n "$BUILD" ] && CMAKE_ARGS="${CMAKE_ARGS} -DRTAGS_BUILD_CLANG=1"
|
||||
|
||||
echo -e "Running cmake:\n\"$CMAKE\" \"$(dirname ${BASH_SOURCE[0]})\" ${CMAKE_ARGS}\n"
|
||||
rm -f CMakeCache.txt
|
||||
eval "$CMAKE" "$(dirname ${BASH_SOURCE[0]})" ${CMAKE_ARGS}
|
||||
@@ -1,386 +0,0 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
|
||||
.TH RC "7" "June 2018" "rc 2.18.125" "User Commands"
|
||||
.SH NAME
|
||||
rc \- manual page for rc 2.18.125
|
||||
.SH SYNOPSIS
|
||||
rc [OPTION]...
|
||||
.SH DESCRIPTION
|
||||
rc is the RTags client application.
|
||||
.PP
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-\-verbose\fR|\-v
|
||||
Be more verbose.
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
Print current version.
|
||||
.TP
|
||||
\fB\-\-verify\-version\fR [arg]
|
||||
Verify that the correct protocol version is used.
|
||||
.TP
|
||||
\fB\-\-silent\fR|\-Q
|
||||
Be silent.
|
||||
.TP
|
||||
\fB\-\-help\fR|\-h
|
||||
Display this help.
|
||||
.TP
|
||||
\fB\-\-config\fR [arg]
|
||||
Use this file (instead of ~/.rcrc).
|
||||
.TP
|
||||
\fB\-\-no\-rc\fR
|
||||
Don't load any rc files.
|
||||
.SS "Rdm:"
|
||||
.TP
|
||||
\fB\-\-quit\-rdm\fR|\-q
|
||||
Tell server to shut down with optional exit code as argument.
|
||||
.TP
|
||||
\fB\-\-connect\-timeout\fR [arg]
|
||||
Timeout for connecting to rdm in ms (default 1000).
|
||||
.SS "Project management:"
|
||||
.TP
|
||||
\fB\-\-clear\fR|\-C
|
||||
Clear projects.
|
||||
.TP
|
||||
\fB\-\-project\fR|\-w [optional]
|
||||
With arg, select project matching that if unique, otherwise list all projects.
|
||||
.TP
|
||||
\fB\-\-delete\-project\fR|\-W [arg]
|
||||
Delete all projects matching regex.
|
||||
.TP
|
||||
\fB\-\-job\-count\fR|\-j [optional]
|
||||
Set or query current job count. (Prefix with l to set low\-priority\-job\-count).
|
||||
.SS "Indexing commands:"
|
||||
.TP
|
||||
\fB\-\-compile\fR|\-c [optional]
|
||||
Pass compilation arguments to rdm.
|
||||
.TP
|
||||
\fB\-\-guess\-flags\fR
|
||||
Guess compile flags (used with \fB\-c\fR).
|
||||
.TP
|
||||
\fB\-\-load\-compile\-commands\fR|\-J [optional]
|
||||
Load compile_commands.json from directory
|
||||
.TP
|
||||
\fB\-\-suspend\fR|\-X [optional]
|
||||
Dump suspended files (don't track changes in these files) with no arg. Otherwise toggle suspension for arg.
|
||||
.SS "Query commands:"
|
||||
.TP
|
||||
\fB\-\-follow\-location\fR|\-f [arg]
|
||||
Follow this location.
|
||||
.TP
|
||||
\fB\-\-references\-name\fR|\-R [arg]
|
||||
Find references matching arg.
|
||||
.TP
|
||||
\fB\-\-references\fR|\-r [arg]
|
||||
Find references matching this location.
|
||||
.TP
|
||||
\fB\-\-list\-symbols\fR|\-S [optional]
|
||||
List symbol names matching arg.
|
||||
.TP
|
||||
\fB\-\-find\-symbols\fR|\-F [optional]
|
||||
Find symbols matching arg.
|
||||
.TP
|
||||
\fB\-\-symbol\-info\fR|\-U [arg]
|
||||
Get cursor info for this location.
|
||||
.TP
|
||||
\fB\-\-status\fR|\-s [optional]
|
||||
Dump status of rdm. Arg can be symbols or symbolNames.
|
||||
.TP
|
||||
\fB\-\-diagnose\fR [arg]
|
||||
Resend diagnostics for file.
|
||||
.TP
|
||||
\fB\-\-diagnose\-all\fR
|
||||
Resend diagnostics for all files.
|
||||
.TP
|
||||
\fB\-\-last\-indexed\fR
|
||||
Get timestamp of the last time indexing completed for the current project.
|
||||
.TP
|
||||
\fB\-\-is\-indexed\fR|\-T [arg]
|
||||
Check if rtags knows about, and is ready to return information about, this source file.
|
||||
.TP
|
||||
\fB\-\-is\-indexing\fR
|
||||
Check if rtags is currently indexing files.
|
||||
.TP
|
||||
\fB\-\-has\-filemanager\fR [optional]
|
||||
Check if rtags has info about files in this directory.
|
||||
.TP
|
||||
\fB\-\-preprocess\fR|\-E [arg]
|
||||
Preprocess file.
|
||||
.TP
|
||||
\fB\-\-reindex\fR|\-V [optional]
|
||||
Reindex all files or all files matching pattern.
|
||||
.TP
|
||||
\fB\-\-check\-reindex\fR|\-x [optional]
|
||||
Check if reindexing is necessary for all files matching pattern.
|
||||
.TP
|
||||
\fB\-\-path\fR|\-P [optional]
|
||||
Print files matching pattern.
|
||||
.TP
|
||||
\fB\-\-current\-project\fR
|
||||
Print path for current project.
|
||||
.TP
|
||||
\fB\-\-dump\-file\fR|\-d [arg]
|
||||
Dump source file.
|
||||
.TP
|
||||
\fB\-\-check\-includes\fR [arg]
|
||||
Check includes for source file.
|
||||
.TP
|
||||
\fB\-\-dump\-file\-maps\fR [arg]
|
||||
Dump file maps for file.
|
||||
.TP
|
||||
\fB\-\-generate\-test\fR [arg]
|
||||
Generate a test for a given source file.
|
||||
.TP
|
||||
\fB\-\-rdm\-log\fR|\-g
|
||||
Receive logs from rdm.
|
||||
.TP
|
||||
\fB\-\-fixits\fR [arg]
|
||||
Get fixits for file.
|
||||
.TP
|
||||
\fB\-\-remove\fR|\-D [arg]
|
||||
Remove file from project.
|
||||
.TP
|
||||
\fB\-\-find\-project\-root\fR [arg]
|
||||
Use to check behavior of find\-project\-root.
|
||||
.TP
|
||||
\fB\-\-find\-project\-build\-root\fR [arg]
|
||||
Use to check behavior of find\-project\-root for builds.
|
||||
.TP
|
||||
\fB\-\-include\-file\fR [arg]
|
||||
Use to generate include statement for symbol.
|
||||
.TP
|
||||
\fB\-\-sources\fR [optional]
|
||||
Dump sources for source file.
|
||||
.TP
|
||||
\fB\-\-dependencies\fR [arg]
|
||||
Dump dependencies for source file [(includes, included\-by, depends\-on, depended\-on, tree\-depends\-on, raw)].
|
||||
.TP
|
||||
\fB\-\-all\-dependencies\fR
|
||||
Dump dependencies for all source files [(includes, included\-by, depends\-on, depended\-on, tree\-depends\-on, raw)].
|
||||
.TP
|
||||
\fB\-\-reload\-file\-manager\fR|\-B
|
||||
Reload file manager.
|
||||
.TP
|
||||
\fB\-\-man\fR
|
||||
Output XML for xmltoman to generate man page for rc :\-)
|
||||
.TP
|
||||
\fB\-\-code\-complete\-at\fR|\-l [arg]
|
||||
Code complete at location: arg is file:line:col.
|
||||
.TP
|
||||
\fB\-\-send\-diagnostics\fR [arg]
|
||||
Only for debugging. Send data to all \fB\-G\fR connections.
|
||||
.TP
|
||||
\fB\-\-dump\-completions\fR
|
||||
Dump cached completions.
|
||||
.TP
|
||||
\fB\-\-dump\-compile\-commands\fR
|
||||
Dump compilation database for project.
|
||||
.TP
|
||||
\fB\-\-set\-buffers\fR [optional]
|
||||
Set active buffers (list of filenames for active buffers in editor).
|
||||
.TP
|
||||
\fB\-\-list\-buffers\fR
|
||||
List active buffers.
|
||||
.TP
|
||||
\fB\-\-add\-buffers\fR [arg]
|
||||
Add additional buffers.
|
||||
.TP
|
||||
\fB\-\-remove\-buffers\fR [arg]
|
||||
Remove buffers.
|
||||
.TP
|
||||
\fB\-\-list\-cursor\-kinds\fR
|
||||
List spelling for known cursor kinds.
|
||||
.TP
|
||||
\fB\-\-class\-hierarchy\fR [arg]
|
||||
Dump class hierarcy for struct/class at location.
|
||||
.TP
|
||||
\fB\-\-debug\-locations\fR [optional]
|
||||
Manipulate debug locations.
|
||||
.TP
|
||||
\fB\-\-validate\fR
|
||||
Validate database files for current project.
|
||||
.TP
|
||||
\fB\-\-tokens\fR [arg]
|
||||
Dump tokens for file. \fB\-\-tokens\fR file.cpp:123\-321 for range.
|
||||
.SS "Command flags:"
|
||||
.TP
|
||||
\fB\-\-strip\-paren\fR|\-p
|
||||
Strip parens in various contexts.
|
||||
.TP
|
||||
\fB\-\-max\fR|\-M [arg]
|
||||
Max lines of output for queries.
|
||||
.TP
|
||||
\fB\-\-reverse\-sort\fR|\-O
|
||||
Sort output reversed.
|
||||
.TP
|
||||
\fB\-\-rename\fR
|
||||
Used for \fB\-\-references\fR to indicate that we're using the results to rename symbols.
|
||||
.TP
|
||||
\fB\-\-unsaved\-file\fR [arg]
|
||||
Pass unsaved file on command line. E.g. \fB\-\-unsaved\-file\fR=\fI\,main\/\fR.cpp:1200 then write 1200 bytes on stdin.
|
||||
.TP
|
||||
\fB\-\-log\-file\fR|\-L [arg]
|
||||
Log to this file.
|
||||
.TP
|
||||
\fB\-\-no\-context\fR|\-N
|
||||
Don't print context for locations.
|
||||
.TP
|
||||
\fB\-\-path\-filter\fR|\-i [arg]
|
||||
Filter out results not matching with arg.
|
||||
.TP
|
||||
\fB\-\-dependency\-filter\fR [arg]
|
||||
Filter out results unless argument depends on them.
|
||||
.TP
|
||||
\fB\-\-range\-filter\fR [arg]
|
||||
Filter out results not in the specified range.
|
||||
.TP
|
||||
\fB\-\-filter\-system\-headers\fR|\-H
|
||||
Don't exempt system headers from path filters.
|
||||
.TP
|
||||
\fB\-\-all\-references\fR|\-e
|
||||
Include definitions/declarations/constructors/destructors for references. Used for rename symbol.
|
||||
.TP
|
||||
\fB\-\-target\-usrs\fR
|
||||
Print all matching usr targets for \fB\-f\fR. Used for debugging.
|
||||
.TP
|
||||
\fB\-\-all\-targets\fR
|
||||
Print multiple targets for \fB\-f\fR. Sorted by best match.
|
||||
.TP
|
||||
\fB\-\-elisp\fR|\-Y
|
||||
Output elisp: (list "one" "two" ...).
|
||||
.TP
|
||||
\fB\-\-json\fR
|
||||
Output json.
|
||||
.TP
|
||||
\fB\-\-diagnostics\fR|\-m
|
||||
Receive async formatted diagnostics from rdm.
|
||||
.TP
|
||||
\fB\-\-match\-regexp\fR|\-Z
|
||||
Treat various text patterns as regexps (\fB\-P\fR, \fB\-i\fR, \fB\-V\fR, \fB\-F\fR).
|
||||
.TP
|
||||
\fB\-\-match\-icase\fR|\-I
|
||||
Match case insensitively
|
||||
.TP
|
||||
\fB\-\-absolute\-path\fR|\-K
|
||||
Print files with absolute path.
|
||||
.TP
|
||||
\fB\-\-socket\-file\fR|\-n [arg]
|
||||
Use this socket file (default ~/.rdm).
|
||||
.TP
|
||||
\fB\-\-socket\-address\fR [arg]
|
||||
Use this host:port combination (instead of \fB\-\-socket\-file\fR).
|
||||
.TP
|
||||
\fB\-\-timeout\fR|\-y [arg]
|
||||
Max time in ms to wait for job to finish (default no timeout).
|
||||
.TP
|
||||
\fB\-\-find\-virtuals\fR|\-k
|
||||
Use in combinations with \fB\-R\fR or \fB\-r\fR to show other implementations of this function.
|
||||
.TP
|
||||
\fB\-\-find\-file\-prefer\-exact\fR|\-A
|
||||
Use to make \fB\-\-find\-file\fR prefer exact matches over partial matches.
|
||||
.TP
|
||||
\fB\-\-symbol\-info\-include\-parents\fR
|
||||
Use to make \fB\-\-symbol\-info\fR include parent symbols.
|
||||
.TP
|
||||
\fB\-\-symbol\-info\-include\-targets\fR
|
||||
Use to make \fB\-\-symbol\-info\fR include target symbols.
|
||||
.TP
|
||||
\fB\-\-symbol\-info\-include\-references\fR
|
||||
Use to make \fB\-\-symbol\-info\fR include reference symbols.
|
||||
.TP
|
||||
\fB\-\-symbol\-info\-include\-base\-classes\fR
|
||||
Use to make \fB\-\-symbol\-info\fR include baseclasses' symbols.
|
||||
.TP
|
||||
\fB\-\-cursor\-kind\fR
|
||||
Include cursor kind in \fB\-\-find\-symbols\fR output.
|
||||
.TP
|
||||
\fB\-\-display\-name\fR
|
||||
Include display name in \fB\-\-find\-symbols\fR output.
|
||||
.TP
|
||||
\fB\-\-current\-file\fR [arg]
|
||||
Pass along which file is being edited to give rdm a better chance at picking the right project.
|
||||
.TP
|
||||
\fB\-\-declaration\-only\fR|\-G
|
||||
Filter out definitions (unless inline).
|
||||
.TP
|
||||
\fB\-\-definition\-only\fR
|
||||
Filter out declarations (unless inline).
|
||||
.TP
|
||||
\fB\-\-kind\-filter\fR [arg]
|
||||
Only return results matching this kind.
|
||||
.TP
|
||||
\fB\-\-containing\-function\fR|\-o
|
||||
Include name of containing function in output.
|
||||
.TP
|
||||
\fB\-\-containing\-function\-location\fR
|
||||
Include location of containing function in output.
|
||||
.TP
|
||||
\fB\-\-build\-index\fR [arg]
|
||||
For sources with multiple builds, use the arg'th.
|
||||
.TP
|
||||
\fB\-\-compilation\-flags\-only\fR
|
||||
For \fB\-\-source\fR, only print compilation flags.
|
||||
.TP
|
||||
\fB\-\-compilation\-flags\-split\-line\fR
|
||||
For \fB\-\-source\fR, print one compilation flag per line.
|
||||
.TP
|
||||
\fB\-\-compilation\-flags\-pwd\fR
|
||||
For \fB\-\-source\fR, print pwd for compile command on the first line.
|
||||
.TP
|
||||
\fB\-\-dump\-include\-headers\fR
|
||||
For \fB\-\-dump\-file\fR, also dump dependencies.
|
||||
.TP
|
||||
\fB\-\-silent\-query\fR
|
||||
Don't log this request in rdm.
|
||||
.TP
|
||||
\fB\-\-synchronous\-completions\fR
|
||||
Wait for completion results and print them to stdout.
|
||||
.TP
|
||||
\fB\-\-synchronous\-diagnostics\fR
|
||||
Wait for diagnostics and print them to stdout.
|
||||
.TP
|
||||
\fB\-\-xml\fR
|
||||
Output XML
|
||||
.TP
|
||||
\fB\-\-no\-sort\-references\-by\-input\fR
|
||||
Don't sort references by input position.
|
||||
.TP
|
||||
\fB\-\-project\-root\fR [arg]
|
||||
Override project root for compile commands.
|
||||
.TP
|
||||
\fB\-\-rtags\-config\fR [arg]
|
||||
Print out .rtags\-config for argument.
|
||||
.TP
|
||||
\fB\-\-wildcard\-symbol\-names\fR|\-a
|
||||
Expand * like wildcards in \fB\-\-list\-symbols\fR and \fB\-\-find\-symbols\fR.
|
||||
.TP
|
||||
\fB\-\-no\-color\fR|\-z
|
||||
Don't colorize context.
|
||||
.TP
|
||||
\fB\-\-wait\fR
|
||||
Wait for reindexing to finish.
|
||||
.TP
|
||||
\fB\-\-code\-complete\-include\-macros\fR
|
||||
Include macros in code completion results.
|
||||
.TP
|
||||
\fB\-\-code\-complete\-includes\fR
|
||||
Give includes in completion results.
|
||||
.TP
|
||||
\fB\-\-code\-complete\-no\-wait\fR
|
||||
Don't wait for synchronous completion if the translation unit has to be created.
|
||||
.TP
|
||||
\fB\-\-code\-complete\-prefix\fR [arg]
|
||||
Filter out code completion results that don't start with this prefix.
|
||||
.TP
|
||||
\fB\-\-code\-completion\-enabled\fR|\-b
|
||||
Inform rdm that we're code\-completing. Use with \fB\-\-diagnose\fR
|
||||
.TP
|
||||
\fB\-\-no\-spell\-checking\fR
|
||||
Don't produce spell check info in diagnostics.
|
||||
.TP
|
||||
\fB\-\-tokens\-include\-symbols\fR
|
||||
Include symbols for tokens.
|
||||
.TP
|
||||
\fB\-\-no\-realpath\fR
|
||||
Don't resolve paths using realpath(3).
|
||||
.SH "SEE ALSO"
|
||||
rdm(7)
|
||||
@@ -1,272 +0,0 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
|
||||
.TH RDM "7" "June 2018" "rdm 2.18.125" "User Commands"
|
||||
.SH NAME
|
||||
rdm \- manual page for rdm 2.18.125
|
||||
.SH SYNOPSIS
|
||||
rdm [OPTION]...
|
||||
.SH DESCRIPTION
|
||||
RTags is a client/server application that indexes C/C++ code and keeps
|
||||
a persistent file\-based database of references, declarations,
|
||||
definitions, symbolnames etc. There's also limited support for
|
||||
ObjC/ObjC++. It allows you to find symbols by name (including nested
|
||||
class and namespace scope). Most importantly we give you proper
|
||||
follow\-symbol and find\-references support. We also have neat little
|
||||
things like rename\-symbol, integration with clang's "fixits"
|
||||
(http://clang.llvm.org/diagnostics.html). We also integrate with
|
||||
flymake using clang's vastly superior errors and warnings. Since
|
||||
RTags constantly will reindex "dirty" files you get live updates of
|
||||
compiler errors and warnings. Since we already know how to compile
|
||||
your sources we have a way to quickly bring up the preprocessed output
|
||||
of the current source file in a buffer.
|
||||
|
||||
While existing taggers like gnu global, cscope, etags, ctags etc do a
|
||||
decent job for C they often fall a little bit short for C++. With its
|
||||
incredible lexical complexity, parsing C++ is an incredibly hard task
|
||||
and we make no bones about the fact that the only reason we are able
|
||||
to improve on the current tools is because of clang
|
||||
(http://clang.llvm.org/). RTags is named RTags in recognition of
|
||||
Roberto Raggi on whose C++ parser we intended to base this project but
|
||||
he assured us clang was the way to go. The name stuck though.
|
||||
.PP
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-\-help\fR|\-h
|
||||
Display this page.
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
Display version.
|
||||
.TP
|
||||
\fB\-\-include\-path\fR|\-I [arg]
|
||||
Add additional include path to clang.
|
||||
.TP
|
||||
\fB\-\-no\-libclang\-include\-path\fR
|
||||
Don't use the include path from libclang.
|
||||
.TP
|
||||
\fB\-\-isystem\fR|\-s [arg]
|
||||
Add additional system include path to clang.
|
||||
.TP
|
||||
\fB\-\-define\fR|\-D [arg]
|
||||
Add additional define directive to clang.
|
||||
.TP
|
||||
\fB\-\-default\-argument\fR [arg]
|
||||
Add additional argument to clang.
|
||||
.TP
|
||||
\fB\-\-log\-file\fR|\-L [arg]
|
||||
Log to this file.
|
||||
.TP
|
||||
\fB\-\-crash\-dump\-file\fR [arg]
|
||||
File to dump crash log to (default is <datadir>/crash.dump).
|
||||
.TP
|
||||
\fB\-\-setenv\fR|\-e [arg]
|
||||
Set this environment variable (\fB\-\-setenv\fR "foobar=1").
|
||||
.TP
|
||||
\fB\-\-no\-Wall\fR|\-W
|
||||
Don't use \fB\-Wall\fR.
|
||||
.TP
|
||||
\fB\-\-Weverything\fR|\-u
|
||||
Use \fB\-Weverything\fR.
|
||||
.TP
|
||||
\fB\-\-verbose\fR|\-v
|
||||
Change verbosity, multiple \fB\-v\fR's are allowed.
|
||||
.TP
|
||||
\fB\-\-job\-count\fR|\-j [arg]
|
||||
Spawn this many concurrent processes for indexing.
|
||||
.TP
|
||||
\fB\-\-header\-error\-job\-count\fR|\-H [arg]
|
||||
Allow this many concurrent header error jobs (default std::max(1, \fB\-\-job\-count\fR / 2)).
|
||||
.TP
|
||||
\fB\-\-test\fR|\-t [arg]
|
||||
Run this test.
|
||||
.TP
|
||||
\fB\-\-test\-timeout\fR|\-z [arg]
|
||||
Timeout for test to complete.
|
||||
.TP
|
||||
\fB\-\-clean\-slate\fR|\-C
|
||||
Clear out all data.
|
||||
.TP
|
||||
\fB\-\-disable\-sighandler\fR|\-x
|
||||
Disable signal handler to dump stack for crashes.
|
||||
.TP
|
||||
\fB\-\-silent\fR|\-S
|
||||
No logging to stdout/stderr.
|
||||
.TP
|
||||
\fB\-\-exclude\-filter\fR|\-X [arg]
|
||||
Files to exclude from rdm, default "*/CMakeFiles/*;*/cmake*/Modules/*;*/conftest.c*;/tmp/*;/private/tmp/*;/private/var/*".
|
||||
.TP
|
||||
\fB\-\-socket\-file\fR|\-n [arg]
|
||||
Use this file for the server socket (default ~/.rdm).
|
||||
.TP
|
||||
\fB\-\-data\-dir\fR|\-d [arg]
|
||||
Use this directory to store persistent data (default \fI\,$XDG_CACHE_HOME/rtags\/\fP otherwise ~/.cache/rtags).
|
||||
.TP
|
||||
\fB\-\-ignore\-printf\-fixits\fR|\-F
|
||||
Disregard any clang fixit that looks like it's trying to fix format for printf and friends.
|
||||
.TP
|
||||
\fB\-\-error\-limit\fR|\-f [arg]
|
||||
Set error limit to argument (\fB\-ferror\-limit=\fR{arg} (default 50).
|
||||
.TP
|
||||
\fB\-\-block\-argument\fR|\-G [arg]
|
||||
Block this argument from being passed to clang. E.g. rdm \fB\-\-block\-argument\fR \fB\-fno\-inline\fR
|
||||
.TP
|
||||
\fB\-\-no\-spell\-checking\fR|\-l
|
||||
Don't pass \fB\-fspell\-checking\fR.
|
||||
.TP
|
||||
\fB\-\-large\-by\-value\-copy\fR|\-r [arg]
|
||||
Use \fB\-Wlarge\-by\-value\-copy\fR=\fI\,[arg]\/\fR when invoking clang.
|
||||
.TP
|
||||
\fB\-\-allow\-multiple\-sources\fR|\-m
|
||||
Don't merge source files added with \fB\-c\fR.
|
||||
.TP
|
||||
\fB\-\-no\-startup\-project\fR|\-o
|
||||
Don't restore the last current project on startup.
|
||||
.TP
|
||||
\fB\-\-no\-no\-unknown\-warnings\-option\fR|\-Y
|
||||
Don't pass \fB\-Wno\-unknown\-warning\-option\fR.
|
||||
.TP
|
||||
\fB\-\-ignore\-compiler\fR|\-b [arg]
|
||||
Ignore this compiler.
|
||||
.TP
|
||||
\fB\-\-compiler\-wrappers\fR [arg]
|
||||
Consider these filenames compiler wrappers (split on ;), default ccache".
|
||||
.TP
|
||||
\fB\-\-watch\-system\-paths\fR|\-w
|
||||
Watch system paths for changes.
|
||||
.TP
|
||||
\fB\-\-rp\-visit\-file\-timeout\fR|\-Z [arg]
|
||||
Timeout for rp visitfile commands in ms (0 means no timeout) (default 60000).
|
||||
.TP
|
||||
\fB\-\-rp\-indexer\-message\-timeout\fR|\-T [arg]
|
||||
Timeout for rp indexer\-message in ms (0 means no timeout) (default 60000).
|
||||
.TP
|
||||
\fB\-\-rp\-connect\-timeout\fR|\-O [arg]
|
||||
Timeout for connection from rp to rdm in ms (0 means no timeout) (default 0).
|
||||
.TP
|
||||
\fB\-\-rp\-connect\-attempts\fR [arg]
|
||||
Number of times rp attempts to connect to rdm before giving up. (default 3).
|
||||
.TP
|
||||
\fB\-\-rp\-nice\-value\fR|\-a [arg]
|
||||
Nice value to use for rp (nice(2)) (default is no nicing).
|
||||
.TP
|
||||
\fB\-\-suspend\-rp\-on\-crash\fR|\-q
|
||||
Suspend rp in SIGSEGV handler (default off).
|
||||
.TP
|
||||
\fB\-\-rp\-log\-to\-syslog\fR
|
||||
Make rp log to syslog.
|
||||
.TP
|
||||
\fB\-\-start\-suspended\fR|\-Q
|
||||
Start out suspended (no reindexing enabled).
|
||||
.TP
|
||||
\fB\-\-separate\-debug\-and\-release\fR|\-E
|
||||
Normally rdm doesn't consider release and debug as different builds. Pass this if you want it to.
|
||||
.TP
|
||||
\fB\-\-separate\-32\-bit\-and\-64\-bit\fR
|
||||
Normally rdm doesn't consider \fB\-m32\fR and \fB\-m64\fR as different builds. Pass this if you want it to.
|
||||
.HP
|
||||
\fB\-\-ignore\-include\-path\-differences\-in\-usr\fR Don't consider sources that only differ in includepaths within \fI\,/usr\/\fP (not including /usr/home/) as different builds.
|
||||
.TP
|
||||
\fB\-\-max\-crash\-count\fR|\-K [arg]
|
||||
Max number of crashes before giving up a sourcefile (default 5).
|
||||
.TP
|
||||
\fB\-\-max\-socket\-write\-buffer\-size\fR [arg]
|
||||
Max number of bytes buffered after EAGAIN.
|
||||
.TP
|
||||
\fB\-\-completion\-cache\-size\fR|\-i [arg]
|
||||
Number of translation units to cache (default 10).
|
||||
.TP
|
||||
\fB\-\-completion\-no\-filter\fR
|
||||
Don't filter private members and destructors from completions.
|
||||
.TP
|
||||
\fB\-\-completion\-logs\fR
|
||||
Log more info about completions.
|
||||
.TP
|
||||
\fB\-\-max\-include\-completion\-depth\fR [arg]
|
||||
Max recursion depth for header completion (default 3).
|
||||
.TP
|
||||
\fB\-\-allow\-Wpedantic\fR|\-P
|
||||
Don't strip out \fB\-Wpedantic\fR. This can cause problems in certain projects.
|
||||
.TP
|
||||
\fB\-\-allow\-Werror\fR
|
||||
Don't strip out \fB\-Werror\fR and \fB\-Wfatal\-errors\fR. By default these are stripped out.
|
||||
.TP
|
||||
\fB\-\-enable\-compiler\-manager\fR|\-R
|
||||
Query compilers for their actual include paths instead of letting clang use its own.
|
||||
.TP
|
||||
\fB\-\-enable\-NDEBUG\fR|\-g
|
||||
Don't remove \fB\-DNDEBUG\fR from compile lines.
|
||||
.TP
|
||||
\fB\-\-progress\fR|\-p
|
||||
Report compilation progress in diagnostics output.
|
||||
.TP
|
||||
\fB\-\-max\-file\-map\-cache\-size\fR|\-y [arg]
|
||||
Max files to cache per query (Should not exceed maximum number of open file descriptors allowed per process) (default 500).
|
||||
.TP
|
||||
\fB\-\-no\-filemanager\-watch\fR|\-M
|
||||
Don't use a file system watcher for filemanager.
|
||||
.TP
|
||||
\fB\-\-no\-filemanager\fR
|
||||
Don't scan project directory for files. (rc \fB\-P\fR won't work).
|
||||
.TP
|
||||
\fB\-\-no\-file\-lock\fR
|
||||
Disable file locking. Not entirely safe but might improve performance on certain systems.
|
||||
.TP
|
||||
\fB\-\-pch\-enabled\fR
|
||||
Enable PCH (experimental).
|
||||
.TP
|
||||
\fB\-\-no\-filesystem\-watcher\fR|\-B
|
||||
Disable file system watching altogether. Reindexing has to be triggered manually.
|
||||
.TP
|
||||
\fB\-\-arg\-transform\fR|\-V [arg]
|
||||
Use arg to transform arguments. [arg] should be executable with (execv(3)).
|
||||
.TP
|
||||
\fB\-\-no\-comments\fR
|
||||
Don't parse/store doxygen comments.
|
||||
.TP
|
||||
\fB\-\-inactivity\-timeout\fR [arg]
|
||||
Time in seconds after which rdm will quit if there's been no activity (N.B., once rdm has quit, something will need to re\-run it!).
|
||||
.TP
|
||||
\fB\-\-daemon\fR
|
||||
Run as daemon (detach from terminal).
|
||||
.TP
|
||||
\fB\-\-log\-file\-log\-level\fR [arg]
|
||||
Log level for log file (default is error), options are: error, warning, debug or verbose\-debug.
|
||||
.TP
|
||||
\fB\-\-watch\-sources\-only\fR
|
||||
Only watch source files (not dependencies).
|
||||
.TP
|
||||
\fB\-\-debug\-locations\fR
|
||||
Set debug locations.
|
||||
.TP
|
||||
\fB\-\-validate\-file\-maps\fR
|
||||
Spend some time validating project data on startup.
|
||||
.TP
|
||||
\fB\-\-tcp\-port\fR [arg]
|
||||
Listen on this tcp socket (default none).
|
||||
.TP
|
||||
\fB\-\-rp\-path\fR [arg]
|
||||
Path to rp.
|
||||
.TP
|
||||
\fB\-\-log\-timestamp\fR
|
||||
Add timestamp to logs.
|
||||
.TP
|
||||
\fB\-\-log\-flush\fR
|
||||
Flush stderr/stdout after each log.
|
||||
.TP
|
||||
\fB\-\-sandbox\-root\fR [arg]
|
||||
Create index using relative paths by stripping dir (enables copying of tag index db files without need to reindex).
|
||||
.TP
|
||||
\fB\-\-poll\-timer\fR [arg]
|
||||
Poll the database of the current project every <arg> seconds.
|
||||
.TP
|
||||
\fB\-\-no\-realpath\fR
|
||||
Don't use realpath(3) for files
|
||||
.TP
|
||||
\fB\-\-translation\-unit\-cache\fR
|
||||
Cache translation units. Not working yet.
|
||||
.TP
|
||||
\fB\-\-config\fR|\-c [arg]
|
||||
Use this file (instead of ~/.rdmrc).
|
||||
.TP
|
||||
\fB\-\-no\-rc\fR|\-N
|
||||
Don't load any rc files.
|
||||
.SH "SEE ALSO"
|
||||
rc(7)
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
SCRIPT="$(readlink -f ${BASH_SOURCE[0]})"
|
||||
DIR="$(dirname $SCRIPT)"
|
||||
|
||||
# echo "$SCRIPT $DIR"
|
||||
|
||||
MAJOR=`echo $1 | awk -F. '{print $1}'`
|
||||
MINOR=`echo $1 | awk -F. '{print $2}'`
|
||||
|
||||
if ! echo "$1" | grep -q "^[0-9]\+\.[0-9]\+$"; then
|
||||
echo "Bad argument: \"$1\""
|
||||
echo "Usage bump-version.sh 3.22"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SED=sed
|
||||
[ -n "`which gsed`" ] && SED=gsed
|
||||
|
||||
$SED -i"" -e "s,^set(RTAGS_VERSION_MAJOR [0-9]\+),set(RTAGS_VERSION_MAJOR $MAJOR)," -e "s,^set(RTAGS_VERSION_MINOR [0-9]\+),set(RTAGS_VERSION_MINOR $MINOR)," "$DIR/../CMakeLists.txt"
|
||||
$SED -i"" -e "s,^(defconst rtags-package-version \"[0-9]\+\.[0-9]\+\"),(defconst rtags-package-version \"${MAJOR}.${MINOR}\")," "$DIR/../src/rtags.el"
|
||||
$SED -i"" -e "s,https://andersbakken\.github\.io/rtags-releases/rtags-[0-9]\+\.[0-9]\+\.tar\.,https://andersbakken.github.io/rtags-releases/rtags-$MAJOR.$MINOR.tar.,g" "$DIR/../README.org"
|
||||
|
||||
git commit -m "Bump version to ${MAJOR}.${MINOR}" $DIR/../README.org $DIR/../CMakeLists.txt $DIR/../src/rtags.el
|
||||
git tag "v${MAJOR}.${MINOR}"
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
#nstall_name_tool -change "@executable_path/../lib/libclang.3.2.dylib" "@executable_path/../3rdparty/clang_install/lib/libclang.3.2.dylib" rdm
|
||||
|
||||
REAL=$(readlink "$1"/3rdparty/clang_install/lib/libclang.dylib)
|
||||
install_name_tool -change "@executable_path/../lib/$(basename \"$REAL\")" "@executable_path/../3rdparty/clang_install/lib/$(basename \"$REAL\")" $2
|
||||
@@ -1,93 +0,0 @@
|
||||
#!/bin/bash -e
|
||||
# gen-man-pages.sh --- generate man pages
|
||||
#
|
||||
# Copyright (c) Christian Schwarzgruber <c.schwarzgruber.cs@gmail.com>
|
||||
#
|
||||
# This file is part of RTags (http://rtags.net).
|
||||
#
|
||||
# RTags is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or (at
|
||||
# your option) any later version.
|
||||
#
|
||||
# RTags is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
|
||||
#
|
||||
# Description: rc, rdm and help2man need to be in the PATH environment variable.
|
||||
BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "Usage: $(basename $0) RDM_RC_BIN_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MAN_BASE="$BASE_DIR/man/man7"
|
||||
|
||||
# Force simply locale `C`
|
||||
LC_TIME=C
|
||||
|
||||
# Generate the rc manual page.
|
||||
echo 'Generating manual page for "rc"'
|
||||
help2man --no-info -s 7 \
|
||||
-i <(echo "
|
||||
[SYNOPSIS]
|
||||
rc [OPTION]...
|
||||
|
||||
[DESCRIPTION]
|
||||
rc is the RTags client application.
|
||||
|
||||
[SEE ALSO]
|
||||
rdm(7)
|
||||
") "$1/rc" > "$MAN_BASE/rc.7"
|
||||
|
||||
# Generate the rdm manual page.
|
||||
echo 'Generating manual page for "rdm"'
|
||||
help2man --no-info -s 7 \
|
||||
-i <(echo "
|
||||
[SYNOPSIS]
|
||||
rdm [OPTION]...
|
||||
|
||||
[DESCRIPTION]
|
||||
RTags is a client/server application that indexes C/C++ code and keeps
|
||||
a persistent file\-based database of references, declarations,
|
||||
definitions, symbolnames etc. There's also limited support for
|
||||
ObjC/ObjC++. It allows you to find symbols by name (including nested
|
||||
class and namespace scope). Most importantly we give you proper
|
||||
follow\-symbol and find\-references support. We also have neat little
|
||||
things like rename\-symbol, integration with clang's \"fixits\"
|
||||
(http://clang.llvm.org/diagnostics.html). We also integrate with
|
||||
flymake using clang's vastly superior errors and warnings. Since
|
||||
RTags constantly will reindex \"dirty\" files you get live updates of
|
||||
compiler errors and warnings. Since we already know how to compile
|
||||
your sources we have a way to quickly bring up the preprocessed output
|
||||
of the current source file in a buffer.
|
||||
|
||||
While existing taggers like gnu global, cscope, etags, ctags etc do a
|
||||
decent job for C they often fall a little bit short for C++. With its
|
||||
incredible lexical complexity, parsing C++ is an incredibly hard task
|
||||
and we make no bones about the fact that the only reason we are able
|
||||
to improve on the current tools is because of clang
|
||||
(http://clang.llvm.org/). RTags is named RTags in recognition of
|
||||
Roberto Raggi on whose C++ parser we intended to base this project but
|
||||
he assured us clang was the way to go. The name stuck though.
|
||||
|
||||
[SEE ALSO]
|
||||
rc(7)
|
||||
") "$1/rdm" > "$MAN_BASE/rdm.7"
|
||||
|
||||
# Fix-ups
|
||||
sed -ri \
|
||||
-e '/^(rdm|rc) options...$/d' \
|
||||
-e 's/^Options:$/.SH OPTIONS/' \
|
||||
-e 's/^(Path to rp) \(default.*\).$/\1./' \
|
||||
-e '/job\\-count/{
|
||||
n
|
||||
s/(.*indexing) \(default [0-9]+\).$/\1./
|
||||
}' \
|
||||
"$MAN_BASE/rc.7" "$MAN_BASE/rdm.7"
|
||||
exit 0
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
VERSION="$1"
|
||||
[ -z "${VERSION}" ] && VERSION="3.8.1"
|
||||
|
||||
CHECKSUMDIR=`dirname "${BASH_SOURCE[0]}"`/../llvm-checksums/
|
||||
|
||||
LLVM=llvm-${VERSION}.src
|
||||
LLVM_FILE=${LLVM}.tar.xz
|
||||
CLANG=cfe-${VERSION}.src
|
||||
CLANG_FILE=${CLANG}.tar.xz
|
||||
LIBCXX=libcxx-${VERSION}.src
|
||||
LIBCXX_FILE=${LIBCXX}.tar.xz
|
||||
|
||||
function dnload()
|
||||
{
|
||||
local FILE=$1-${VERSION}.src.tar.xz
|
||||
local URL=http://llvm.org/releases/${VERSION}/${FILE}
|
||||
STATUS=`curl -L -w "%{http_code}" -z ${FILE} https://releases.llvm.org/${VERSION}/${FILE} -o ${FILE} 2>/dev/null`
|
||||
echo ${STATUS}
|
||||
}
|
||||
|
||||
STATUS=`dnload llvm`
|
||||
if [ "${STATUS}" == 200 ] || [ ! -d llvm ]; then
|
||||
tar xf ${LLVM_FILE}
|
||||
rm -rf llvm
|
||||
mv ${LLVM} llvm
|
||||
dnload cfe >/dev/null
|
||||
tar xf ${CLANG_FILE}
|
||||
rm -f llvm/tools/clang
|
||||
mv ${CLANG} llvm/tools/clang
|
||||
|
||||
if [ `uname -s` = Darwin ]; then
|
||||
dnload libcxx >/dev/null
|
||||
tar xf ${LIBCXX_FILE}
|
||||
mv ${LIBCXX} llvm/projects/libcxx
|
||||
fi
|
||||
fi
|
||||
@@ -1,292 +0,0 @@
|
||||
#!/bin/bash -
|
||||
#
|
||||
# File: git-archive-all.sh
|
||||
#
|
||||
# Description: A utility script that builds an archive file(s) of all
|
||||
# git repositories and submodules in the current path.
|
||||
# Useful for creating a single tarfile of a git super-
|
||||
# project that contains other submodules.
|
||||
#
|
||||
# Examples: Use git-archive-all.sh to create archive distributions
|
||||
# from git repositories. To use, simply do:
|
||||
#
|
||||
# cd $GIT_DIR; git-archive-all.sh
|
||||
#
|
||||
# where $GIT_DIR is the root of your git superproject.
|
||||
#
|
||||
# License: GPL3
|
||||
#
|
||||
###############################################################################
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
# DEBUGGING
|
||||
set -e
|
||||
set -C # noclobber
|
||||
|
||||
# TRAP SIGNALS
|
||||
trap 'cleanup' QUIT EXIT
|
||||
|
||||
# For security reasons, explicitly set the internal field separator
|
||||
# to newline, space, tab
|
||||
OLD_IFS=$IFS
|
||||
IFS='
|
||||
'
|
||||
|
||||
function cleanup () {
|
||||
rm -f $TMPFILE
|
||||
rm -f $TOARCHIVE
|
||||
IFS="$OLD_IFS"
|
||||
}
|
||||
|
||||
function usage () {
|
||||
echo "Usage is as follows:"
|
||||
echo
|
||||
echo "$PROGRAM <--version>"
|
||||
echo " Prints the program version number on a line by itself and exits."
|
||||
echo
|
||||
echo "$PROGRAM <--usage|--help|-?>"
|
||||
echo " Prints this usage output and exits."
|
||||
echo
|
||||
echo "$PROGRAM [--format <fmt>] [--prefix <path>] [--verbose|-v] [--separate|-s]"
|
||||
echo " [--tree-ish|-t <tree-ish>] [output_file]"
|
||||
echo " Creates an archive for the entire git superproject, and its submodules"
|
||||
echo " using the passed parameters, described below."
|
||||
echo
|
||||
echo " If '--format' is specified, the archive is created with the named"
|
||||
echo " git archiver backend. Obviously, this must be a backend that git archive"
|
||||
echo " understands. The format defaults to 'tar' if not specified."
|
||||
echo
|
||||
echo " If '--prefix' is specified, the archive's superproject and all submodules"
|
||||
echo " are created with the <path> prefix named. The default is to not use one."
|
||||
echo
|
||||
echo " If '--separate' or '-s' is specified, individual archives will be created"
|
||||
echo " for each of the superproject itself and its submodules. The default is to"
|
||||
echo " concatenate individual archives into one larger archive."
|
||||
echo
|
||||
echo " If '--tree-ish' is specified, the archive will be created based on whatever"
|
||||
echo " you define the tree-ish to be. Branch names, commit hash, etc. are acceptable."
|
||||
echo " Defaults to HEAD if not specified. See git archive's documentation for more"
|
||||
echo " information on what a tree-ish is."
|
||||
echo
|
||||
echo " If 'output_file' is specified, the resulting archive is created as the"
|
||||
echo " file named. This parameter is essentially a path that must be writeable."
|
||||
echo " When combined with '--separate' ('-s') this path must refer to a directory."
|
||||
echo " Without this parameter or when combined with '--separate' the resulting"
|
||||
echo " archive(s) are named with a dot-separated path of the archived directory and"
|
||||
echo " a file extension equal to their format (e.g., 'superdir.submodule1dir.tar')."
|
||||
echo
|
||||
echo " The special value '-' (single dash) is treated as STDOUT and, when used, the"
|
||||
echo " --separate option is ignored. Use a double-dash to separate the outfile from"
|
||||
echo " the value of previous options. For example, to write a .zip file to STDOUT:"
|
||||
echo
|
||||
echo " ./$PROGRAM --format zip -- -"
|
||||
echo
|
||||
echo " If '--verbose' or '-v' is specified, progress will be printed."
|
||||
}
|
||||
|
||||
function version () {
|
||||
echo "$PROGRAM version $VERSION"
|
||||
}
|
||||
|
||||
# Internal variables and initializations.
|
||||
readonly PROGRAM=$(basename "$0")
|
||||
readonly VERSION=0.3
|
||||
|
||||
SEPARATE=0
|
||||
VERBOSE=0
|
||||
|
||||
TARCMD=$(command -v gnutar || command -v gtar || command -v tar)
|
||||
FORMAT=tar
|
||||
PREFIX=
|
||||
TREEISH=HEAD
|
||||
|
||||
# RETURN VALUES/EXIT STATUS CODES
|
||||
readonly E_BAD_OPTION=254
|
||||
readonly E_UNKNOWN=255
|
||||
|
||||
# Process command-line arguments.
|
||||
while test $# -gt 0; do
|
||||
if [ x"$1" == x"--" ]; then
|
||||
# detect argument termination
|
||||
shift
|
||||
break
|
||||
fi
|
||||
case $1 in
|
||||
--format )
|
||||
shift
|
||||
FORMAT="$1"
|
||||
shift
|
||||
;;
|
||||
|
||||
--prefix )
|
||||
shift
|
||||
PREFIX="$1"
|
||||
shift
|
||||
;;
|
||||
|
||||
--separate | -s )
|
||||
shift
|
||||
SEPARATE=1
|
||||
;;
|
||||
|
||||
--tree-ish | -t )
|
||||
shift
|
||||
TREEISH="$1"
|
||||
shift
|
||||
;;
|
||||
|
||||
--version )
|
||||
version
|
||||
exit
|
||||
;;
|
||||
|
||||
--verbose | -v )
|
||||
shift
|
||||
VERBOSE=1
|
||||
;;
|
||||
|
||||
-? | --usage | --help )
|
||||
usage
|
||||
exit
|
||||
;;
|
||||
|
||||
-* )
|
||||
echo "Unrecognized option: $1" >&2
|
||||
usage
|
||||
exit $E_BAD_OPTION
|
||||
;;
|
||||
|
||||
* )
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
OLD_PWD=$(pwd)
|
||||
TMPDIR=${TMPDIR:-/tmp}
|
||||
TMPFILE=$(mktemp "$TMPDIR/$PROGRAM.XXXXXX") # Create a place to store our work's progress
|
||||
TOARCHIVE=$(mktemp "$TMPDIR/$PROGRAM.toarchive.XXXXXX")
|
||||
OUT_FILE=$OLD_PWD # assume "this directory" without a name change by default
|
||||
|
||||
if [ ! -z "$1" ]; then
|
||||
OUT_FILE="$1"
|
||||
if [ "-" == $OUT_FILE ]; then
|
||||
SEPARATE=0
|
||||
fi
|
||||
shift
|
||||
fi
|
||||
|
||||
# Validate parameters; error early, error often.
|
||||
if [ "-" == $OUT_FILE -o $SEPARATE -ne 1 ] && [ "$FORMAT" == "tar" -a $($TARCMD --help | grep -q -- "--concatenate"; echo $?) -ne 0 ]; then
|
||||
echo "Your 'tar' does not support the '--concatenate' option, which we need"
|
||||
echo "to produce a single tarfile. Either install a compatible tar (such as"
|
||||
echo "gnutar), or invoke $PROGRAM with the '--separate' option."
|
||||
exit
|
||||
elif [ $SEPARATE -eq 1 -a ! -d $OUT_FILE ]; then
|
||||
echo "When creating multiple archives, your destination must be a directory."
|
||||
echo "If it's not, you risk being surprised when your files are overwritten."
|
||||
exit
|
||||
elif [ $(git config -l | grep -q '^core\.bare=false'; echo $?) -ne 0 ]; then
|
||||
echo "$PROGRAM must be run from a git working copy (i.e., not a bare repository)."
|
||||
exit
|
||||
fi
|
||||
|
||||
# Create the superproject's git-archive
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
echo -n "creating superproject archive..."
|
||||
fi
|
||||
git archive --format=$FORMAT --prefix="$PREFIX" $TREEISH > "$TMPDIR/$(basename "$(pwd)").$FORMAT"
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
echo "done"
|
||||
fi
|
||||
echo "$TMPDIR/$(basename "$(pwd)").$FORMAT" >| $TMPFILE # clobber on purpose
|
||||
superfile=$(head -n 1 $TMPFILE)
|
||||
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
echo -n "looking for subprojects..."
|
||||
fi
|
||||
# find all '.git' dirs, these show us the remaining to-be-archived dirs
|
||||
# we only want directories that are below the current directory
|
||||
find . -mindepth 2 -name '.git' -type d -print | sed -e 's/^\.\///' -e 's/\.git$//' >> $TOARCHIVE
|
||||
# as of version 1.7.8, git places the submodule .git directories under the superprojects .git dir
|
||||
# the submodules get a .git file that points to their .git dir. we need to find all of these too
|
||||
find . -mindepth 2 -name '.git' -type f -print0 | xargs -0 grep -l "gitdir" | sed -e 's/^\.\///' -e 's/\.git$//' >> $TOARCHIVE
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
echo "done"
|
||||
echo " found:"
|
||||
while read arch
|
||||
do
|
||||
echo " $arch"
|
||||
done < $TOARCHIVE
|
||||
fi
|
||||
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
echo -n "archiving submodules..."
|
||||
fi
|
||||
while read path; do
|
||||
TREEISH=$(git submodule | grep "^ .*${path%/} " | cut -d ' ' -f 2) # git submodule does not list trailing slashes in $path
|
||||
cd "$path"
|
||||
git archive --format=$FORMAT --prefix="${PREFIX}$path" ${TREEISH:-HEAD} > "$TMPDIR"/"$(echo "$path" | sed -e 's/\//./g')"$FORMAT
|
||||
if [ $FORMAT == 'zip' ]; then
|
||||
# delete the empty directory entry; zipped submodules won't unzip if we don't do this
|
||||
zip -d "$(tail -n 1 $TMPFILE)" "${PREFIX}${path%/}" >/dev/null # remove trailing '/'
|
||||
fi
|
||||
echo "$TMPDIR"/"$(echo "$path" | sed -e 's/\//./g')"$FORMAT >> $TMPFILE
|
||||
cd "$OLD_PWD"
|
||||
done < $TOARCHIVE
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
echo "done"
|
||||
fi
|
||||
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
echo -n "concatenating archives into single archive..."
|
||||
fi
|
||||
# Concatenate archives into a super-archive.
|
||||
if [ $SEPARATE -eq 0 -o "-" == $OUT_FILE ]; then
|
||||
if [ $FORMAT == 'tar' ]; then
|
||||
sed -e '1d' $TMPFILE | while read file; do
|
||||
$TARCMD --concatenate -f "$superfile" "$file" && rm -f "$file"
|
||||
done
|
||||
elif [ $FORMAT == 'zip' ]; then
|
||||
sed -e '1d' $TMPFILE | while read file; do
|
||||
# zip incorrectly stores the full path, so cd and then grow
|
||||
cd "$(dirname $file)"
|
||||
zip -g "$superfile" "$(basename $file)" && rm -f "$file"
|
||||
done
|
||||
cd "$OLD_PWD"
|
||||
fi
|
||||
|
||||
echo "$superfile" >| $TMPFILE # clobber on purpose
|
||||
fi
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
echo "done"
|
||||
fi
|
||||
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
echo -n "moving archive to $OUT_FILE..."
|
||||
fi
|
||||
while read file; do
|
||||
if [ "-" == $OUT_FILE ]; then
|
||||
cat "$file" && rm -f "$file"
|
||||
else
|
||||
mv "$file" "$OUT_FILE"
|
||||
fi
|
||||
done < $TMPFILE
|
||||
if [ $VERBOSE -eq 1 ]; then
|
||||
echo "done"
|
||||
fi
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
brew tap homebrew/versions
|
||||
brew install llvm38 --with-libcxx --with-clang --without-assertions --rtti $@
|
||||
@@ -1,69 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
### TO RUN
|
||||
### curl https://raw.githubusercontent.com/Andersbakken/rtags/master/scripts/rtags-release.sh | bash
|
||||
|
||||
REPO=`mktemp -d`
|
||||
RELEASES_REPO=`mktemp -d`
|
||||
CMAKE_ARGS="-DRTAGS_ENABLE_DEV_OPTIONS=1"
|
||||
FORCE=
|
||||
|
||||
while [ -n "$1" ]; do
|
||||
case "$1" in
|
||||
--repo|-r)
|
||||
shift
|
||||
REPO="$1"
|
||||
;;
|
||||
--releases-repo|-R)
|
||||
shift
|
||||
RELEASES_REPO="$1"
|
||||
;;
|
||||
--force|-f)
|
||||
FORCE=1
|
||||
;;
|
||||
--cmake-args|-C)
|
||||
shift
|
||||
CMAKE_ARGS="$CMAKE_ARGS $1"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
git clone git@github.com:Andersbakken/rtags.git "$REPO" --recursive --depth 1 || exit 1
|
||||
cd "$REPO"
|
||||
|
||||
branch_name="$(git symbolic-ref HEAD 2>/dev/null)" || branch_name="(unnamed branch)" # detached HEAD
|
||||
branch_name=${branch_name##refs/heads/}
|
||||
if [ "$branch_name" != "master" ]; then
|
||||
echo wrong branch $branch_name
|
||||
exit 1
|
||||
fi
|
||||
|
||||
commit=$(git show --oneline --no-patch)
|
||||
current=`curl --silent http://andersbakken.github.io/rtags-releases/commit | cut -d' ' -f1`
|
||||
if [ "`echo "$commit" | cut -d' ' -f1`" = "$current" -a -z "$FORCE" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git clone git@github.com:Andersbakken/rtags-releases.git "$RELEASES_REPO" --recursive --depth 1 || exit 1
|
||||
cd "$RELEASES_REPO"
|
||||
if ! git branch | grep --quiet "^\* *gh-pages$"; then
|
||||
echo "wrong branch"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -rf .git/hooks/*
|
||||
|
||||
cmake "$REPO" $CMAKE_ARGS >/dev/null || exit 1
|
||||
make package_source >/dev/null || exit 1
|
||||
cmake "$REPO" $CMAKE_ARGS -DCPACK_GENERATOR=TBZ2 >/dev/null 2>&1 || exit 1
|
||||
make package_source >/dev/null || exit 1
|
||||
echo "$commit" > commit
|
||||
git add *.tar.gz *.tar.bz2 commit >/dev/null
|
||||
git commit --amend -m "Release for $commit" >/dev/null
|
||||
git push -f git@github.com:Andersbakken/rtags-releases.git gh-pages >/dev/null
|
||||
rm -rf "$REPO" "$RELEASES_REPO"
|
||||
@@ -1,94 +0,0 @@
|
||||
#!/bin/bash -e
|
||||
# travis.sh --- setup and compile script for travis
|
||||
#
|
||||
# Copyright (c) Christian Schwarzgruber <c.schwarzgruber.cs@gmail.com>
|
||||
# Author: Christian Schwarzgruber
|
||||
# Created: Tue May 10 22:28:28 2016 (+0200)
|
||||
#
|
||||
# This file is part of RTags (http://rtags.net).
|
||||
#
|
||||
# RTags is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# RTags is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
#
|
||||
# Description: The following variables can be changed from the build matrix:
|
||||
# - ASAN (default value is "1")
|
||||
# - LUA_VERSION (default value is "5.3.2")
|
||||
# - LUA_DISABLE (default value is "", set it to anything to disable lua
|
||||
# extension for that matrix)
|
||||
declare -a CMAKE_PARAMS=("-DCMAKE_CXX_COMPILER=$CXX$COMPILER_VERSION"
|
||||
"-DBUILD_TESTING=1"
|
||||
"-DCMAKE_C_COMPILER=$CC$COMPILER_VERSION")
|
||||
|
||||
if [ "$ASAN" ]; then
|
||||
CMAKE_PARAMS+=("-DASAN=address,undefined")
|
||||
fi
|
||||
|
||||
LUA_DISABLE=${LUA_DISABLE:-""}
|
||||
if [ ! $LUA_DISABLE ]; then
|
||||
CMAKE_PARAMS+=("-DLUA_ENABLED=1")
|
||||
echo "Running build with Lua extension."
|
||||
else
|
||||
echo "Running build without Lua extension."
|
||||
fi # end ! $LUA_DISABLE
|
||||
|
||||
function build()
|
||||
{
|
||||
mkdir build && pushd build > /dev/null
|
||||
cmake "${CMAKE_PARAMS[@]}" .. || cat CMakeFiles/CMakeError.log
|
||||
make VERBOSE=1 -j2
|
||||
}
|
||||
|
||||
# All arguments will be passed on to ctest
|
||||
function run_tests()
|
||||
{
|
||||
PATH=$(pwd)/bin:$PATH
|
||||
ctest --output-on-failure --verbose $@
|
||||
}
|
||||
|
||||
function osx()
|
||||
{
|
||||
## Step -- Setup
|
||||
brew update
|
||||
brew install llvm yarn cppunit
|
||||
brew upgrade python3
|
||||
python3 -m pip install --upgrade pip
|
||||
pip3 install --user --upgrade nose PyHamcrest
|
||||
# Add nosetest bin dir to the env path var
|
||||
PATH=$PATH:/Users/travis/Library/Python/3.6/bin
|
||||
|
||||
## Step -- Build
|
||||
build
|
||||
|
||||
## Step -- Test
|
||||
run_tests
|
||||
}
|
||||
|
||||
function gnu_linux()
|
||||
{
|
||||
## Step -- Setup
|
||||
pip3 install --user --upgrade nose PyHamcrest
|
||||
|
||||
## Step -- Build
|
||||
build
|
||||
|
||||
## Step -- Test
|
||||
run_tests
|
||||
}
|
||||
|
||||
if [ $TRAVIS_OS_NAME = osx ]; then
|
||||
osx
|
||||
else
|
||||
gnu_linux
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -1,49 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# chkconfig: 35 90 12
|
||||
# description: Foo server
|
||||
#
|
||||
|
||||
# Get function from functions library
|
||||
. /etc/init.d/functions
|
||||
|
||||
# Start the service FOO
|
||||
start() {
|
||||
initlog -c "echo -n Starting FOO server: "
|
||||
/path/to/FOO &
|
||||
### Create the lock file ###
|
||||
touch /var/lock/subsys/FOO
|
||||
success $"FOO server startup"
|
||||
echo
|
||||
}
|
||||
|
||||
# Restart the service FOO
|
||||
stop() {
|
||||
initlog -c "echo -n Stopping FOO server: "
|
||||
killproc FOO
|
||||
### Now, delete the lock file ###
|
||||
rm -f /var/lock/subsys/FOO
|
||||
echo
|
||||
}
|
||||
|
||||
### main logic ###
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
status)
|
||||
status FOO
|
||||
;;
|
||||
restart|reload|condrestart)
|
||||
stop
|
||||
start
|
||||
;;
|
||||
*)
|
||||
echo $"Usage: $0 {start|stop|restart|reload|status}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit 0
|
||||
@@ -1,11 +0,0 @@
|
||||
[Unit]
|
||||
Description=RTags daemon
|
||||
|
||||
Requires=rdm.socket
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=$RDM -v --inactivity-timeout 300 --log-flush
|
||||
ExecStartPost=/bin/sh -c "echo +19 > /proc/$MAINPID/autogroup"
|
||||
Nice=19
|
||||
CPUSchedulingPolicy=idle
|
||||
@@ -1,8 +0,0 @@
|
||||
[Unit]
|
||||
Description=RTags daemon socket
|
||||
|
||||
[Socket]
|
||||
ListenStream=%t/rdm.socket
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -1,177 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "AST.h"
|
||||
#include "ClangThread.h"
|
||||
#include "selene.h"
|
||||
|
||||
#define TO_STR1(x) #x
|
||||
#define TO_STR(x) TO_STR1(x)
|
||||
|
||||
struct UserData {
|
||||
List<AST::Cursor> parents;
|
||||
AST *ast;
|
||||
};
|
||||
CXChildVisitResult AST::visitor(CXCursor cursor, CXCursor, CXClientData u)
|
||||
{
|
||||
UserData *userData = reinterpret_cast<UserData*>(u);
|
||||
assert(userData);
|
||||
Cursor::Data *p = userData->parents.isEmpty() ? 0 : userData->parents.back().data.get();
|
||||
Cursor c = userData->ast->construct(cursor, p);
|
||||
userData->parents.push_back(Cursor { c.data } );
|
||||
clang_visitChildren(cursor, visitor, u);
|
||||
if (userData->parents.size() > 1)
|
||||
userData->parents.pop_back();
|
||||
return CXChildVisit_Continue;
|
||||
}
|
||||
|
||||
template <typename T> static void assign(sel::Selector selector, const T &t) { selector = t; }
|
||||
void assign(sel::Selector selector, const String &str) { selector = str.ref(); }
|
||||
|
||||
template <typename T>
|
||||
static void exposeArray(sel::Selector selector, const std::vector<T> &array)
|
||||
{
|
||||
int i = 0;
|
||||
for (const T &t : array) {
|
||||
assign(selector[i++], t);
|
||||
}
|
||||
}
|
||||
|
||||
static void registerClasses(sel::State &state)
|
||||
{
|
||||
state["SourceLocation"].SetClass<AST::SourceLocation>("line", &AST::SourceLocation::line,
|
||||
"column", &AST::SourceLocation::column,
|
||||
"file", &AST::SourceLocation::file,
|
||||
"offset", &AST::SourceLocation::offset,
|
||||
"toString", &AST::SourceLocation::toString);
|
||||
state["SourceRange"].SetClass<AST::SourceRange>("start", &AST::SourceRange::start,
|
||||
"end", &AST::SourceRange::end,
|
||||
"length", &AST::SourceRange::length,
|
||||
"toString", &AST::SourceRange::toString);
|
||||
|
||||
auto cursors = state["Cursors"];
|
||||
cursors.SetClass<AST::Cursors>("size", &AST::Cursors::size,
|
||||
"at", &AST::Cursors::at);
|
||||
auto cursor = state["Cursor"];
|
||||
cursor.SetClass<AST::Cursor>("location", &AST::Cursor::location,
|
||||
"usr", &AST::Cursor::usr,
|
||||
"kind", &AST::Cursor::kind,
|
||||
"linkage", &AST::Cursor::linkage,
|
||||
"availability", &AST::Cursor::availability,
|
||||
"language", &AST::Cursor::language,
|
||||
"spelling", &AST::Cursor::spelling,
|
||||
"displayName", &AST::Cursor::displayName,
|
||||
"rawComment", &AST::Cursor::rawComment,
|
||||
"briefComment", &AST::Cursor::briefComment,
|
||||
"mangledName", &AST::Cursor::mangledName,
|
||||
"templateKind", &AST::Cursor::templateKind,
|
||||
"range", &AST::Cursor::range,
|
||||
"children", &AST::Cursor::children,
|
||||
"query", &AST::Cursor::query,
|
||||
"None", &AST::Cursor::none,
|
||||
"Add", &AST::Cursor::add,
|
||||
"Recurse", &AST::Cursor::recurse,
|
||||
"overriddenCount", &AST::Cursor::overriddenCount,
|
||||
"overriddenCursors", &AST::Cursor::overriddenCursors,
|
||||
"argumentCount", &AST::Cursor::argumentCount,
|
||||
"arguments", &AST::Cursor::arguments,
|
||||
"fieldBitWidth", &AST::Cursor::fieldBitWidth,
|
||||
"typedefUnderlyingType", &AST::Cursor::typedefUnderlyingType,
|
||||
"enumIntegerType", &AST::Cursor::enumIntegerType,
|
||||
"enumConstantValue", &AST::Cursor::enumConstantValue,
|
||||
"includedFile", &AST::Cursor::includedFile,
|
||||
"templateArgumentCount", &AST::Cursor::templateArgumentCount,
|
||||
"templateArgumentType", &AST::Cursor::templateArgumentType,
|
||||
"templateArgumentValue", &AST::Cursor::templateArgumentValue,
|
||||
"templateArgumentKind", &AST::Cursor::templateArgumentKind,
|
||||
"referenced", &AST::Cursor::referenced,
|
||||
"canonical", &AST::Cursor::canonical,
|
||||
"lexicalParent", &AST::Cursor::lexicalParent,
|
||||
"semanticParent", &AST::Cursor::semanticParent,
|
||||
"definitionCursor", &AST::Cursor::definitionCursor,
|
||||
"specializedCursorTemplate", &AST::Cursor::specializedCursorTemplate,
|
||||
"childCount", &AST::Cursor::childCount,
|
||||
"child", &AST::Cursor::child,
|
||||
"isBitField", &AST::Cursor::isBitField,
|
||||
"isVirtualBase", &AST::Cursor::isVirtualBase,
|
||||
"isStatic", &AST::Cursor::isStatic,
|
||||
"isVirtual", &AST::Cursor::isVirtual,
|
||||
"isPureVirtual", &AST::Cursor::isPureVirtual,
|
||||
"isConst", &AST::Cursor::isConst,
|
||||
"isDefinition", &AST::Cursor::isDefinition,
|
||||
"isDynamicCall", &AST::Cursor::isDynamicCall);
|
||||
}
|
||||
|
||||
std::shared_ptr<AST> AST::create(const Source &source, const String &sourceCode, CXTranslationUnit unit)
|
||||
{
|
||||
std::shared_ptr<AST> ast(new AST);
|
||||
ast->mState.reset(new sel::State {true});
|
||||
sel::State &state = *ast->mState;
|
||||
registerClasses(state);
|
||||
ast->mSourceCode = sourceCode;
|
||||
state["sourceFile"] = source.sourceFile().ref();
|
||||
state["sourceCode"] = sourceCode.ref();
|
||||
state["write"] = [ast](const std::string &str) {
|
||||
// error() << "writing" << str;
|
||||
ast->mReturnValues.append(str);
|
||||
};
|
||||
|
||||
exposeArray(state["commandLine"], source.toCommandLine(Source::Default|Source::IncludeCompiler|Source::IncludeSourceFile));
|
||||
|
||||
if (unit) {
|
||||
UserData userData;
|
||||
userData.ast = ast.get();
|
||||
visitor(clang_getTranslationUnitCursor(unit), clang_getNullCursor(), &userData);
|
||||
|
||||
const Cursor root = userData.parents.front();
|
||||
state["root"] = [root]() { return root; };
|
||||
state["findByUsr"] = [ast](const std::string &usr) {
|
||||
return ast->mByUsr.value(usr);
|
||||
};
|
||||
|
||||
state["findByOffset"] = [ast](const std::string &str) {
|
||||
// int offset = atoi(str.c_str());
|
||||
// if (offset) {
|
||||
|
||||
// } else
|
||||
// sscanf
|
||||
// return mByUsr.value(usr);
|
||||
};
|
||||
const String script = Path(TO_STR(RTAGS_SOURCE_DIR) "/rtags.lua").readAll();
|
||||
state(script.constData());
|
||||
}
|
||||
return ast;
|
||||
}
|
||||
|
||||
List<AST::Diagnostic> AST::diagnostics() const
|
||||
{
|
||||
return List<Diagnostic>();
|
||||
}
|
||||
|
||||
List<AST::SkippedRange> AST::skippedRanges() const
|
||||
{
|
||||
return List<SkippedRange>();
|
||||
}
|
||||
|
||||
List<String> AST::evaluate(const String &script)
|
||||
{
|
||||
assert(mReturnValues.isEmpty());
|
||||
try {
|
||||
mState->operator()(script.constData());
|
||||
} catch (...) {
|
||||
error() << "Got exception";
|
||||
}
|
||||
return std::move(mReturnValues);
|
||||
}
|
||||
-460
@@ -1,460 +0,0 @@
|
||||
#ifndef AST_h
|
||||
#define AST_h
|
||||
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <clang-c/Index.h>
|
||||
#include "Source.h"
|
||||
#include "Location.h"
|
||||
#include "RTags.h"
|
||||
#include "rct/String.h"
|
||||
#include "rct/List.h"
|
||||
|
||||
namespace sel {
|
||||
class State;
|
||||
};
|
||||
class AST
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
static std::string toString(T t)
|
||||
{
|
||||
String str;
|
||||
Log(&str) << t;
|
||||
return str.ref();
|
||||
}
|
||||
static std::string toString(CXString str)
|
||||
{
|
||||
return RTags::eatString(str).ref();
|
||||
}
|
||||
struct SourceLocation {
|
||||
SourceLocation()
|
||||
: offset(-1)
|
||||
{}
|
||||
|
||||
bool operator<(const SourceLocation &other) const { return location < other.location; }
|
||||
|
||||
bool isNull() const { return location.isNull(); }
|
||||
Location location;
|
||||
int offset;
|
||||
|
||||
int line() const { return location.line(); }
|
||||
int column() const { return location.column(); }
|
||||
std::string file() const { return location.path(); }
|
||||
std::string toString() const
|
||||
{
|
||||
return String::format<256>("%s,%d",
|
||||
location.toString(Location::AbsolutePath|Location::NoColor).constData(),
|
||||
offset);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct SourceRange {
|
||||
SourceRange(const CXSourceRange &r = clang_getNullRange())
|
||||
: range(r)
|
||||
{}
|
||||
CXSourceRange range;
|
||||
|
||||
SourceLocation start() const { return AST::createLocation(clang_getRangeStart(range)); }
|
||||
SourceLocation end() const { return AST::createLocation(clang_getRangeEnd(range)); }
|
||||
int length() const { return end().offset - start().offset; }
|
||||
std::string toString() const { return start().toString() + " - " + end().toString(); }
|
||||
};
|
||||
struct CursorType;
|
||||
struct Cursors;
|
||||
struct Cursor {
|
||||
struct Data : public std::enable_shared_from_this<Data> {
|
||||
Data(AST *a, Data *p, const CXCursor &c, const SourceLocation &loc, const std::string &u = std::string())
|
||||
: ast(a), parent(p), cursor(c), location(loc), usr(u)
|
||||
{
|
||||
}
|
||||
~Data()
|
||||
{
|
||||
for (Data *child : children)
|
||||
delete child;
|
||||
}
|
||||
|
||||
AST *ast;
|
||||
Data *parent;
|
||||
List<Data*> children;
|
||||
CXCursor cursor;
|
||||
SourceLocation location;
|
||||
std::string usr;
|
||||
};
|
||||
|
||||
SourceLocation location() const { return data ? data->location : SourceLocation(); }
|
||||
std::string usr() const { return data ? data->usr : std::string(); }
|
||||
std::string kind() const { return stringProperty(&clang_getCursorKind); }
|
||||
std::string linkage() const { return stringProperty(&clang_getCursorLinkage); }
|
||||
std::string availability() const { return stringProperty(&clang_getCursorAvailability); }
|
||||
std::string language() const { return stringProperty(&clang_getCursorAvailability); }
|
||||
std::string spelling() const { return stringProperty(&clang_getCursorSpelling); }
|
||||
std::string displayName() const { return stringProperty(&clang_getCursorDisplayName); }
|
||||
std::string rawComment() const { return stringProperty(&clang_Cursor_getRawCommentText); }
|
||||
std::string briefComment() const { return stringProperty(&clang_Cursor_getBriefCommentText); }
|
||||
|
||||
std::string mangledName() const
|
||||
{
|
||||
#if CINDEX_VERSION > CINDEX_VERSION_ENCODE(0, 28)
|
||||
return stringProperty(&clang_Cursor_getMangling);
|
||||
#else
|
||||
return std::string();
|
||||
#endif
|
||||
}
|
||||
std::string templateKind() const { return stringProperty(&clang_getTemplateCursorKind); }
|
||||
SourceRange range() const { return data ? SourceRange { clang_getCursorExtent(data->cursor) } : SourceRange(); }
|
||||
unsigned overriddenCount() const
|
||||
{
|
||||
unsigned count = 0;
|
||||
if (data) {
|
||||
CXCursor *overridden = 0;
|
||||
clang_getOverriddenCursors(data->cursor, &overridden, &count);
|
||||
if (overridden)
|
||||
clang_disposeOverriddenCursors(overridden);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
Cursors overriddenCursors() const
|
||||
{
|
||||
Cursors ret;
|
||||
if (data) {
|
||||
CXCursor *overridden = 0;
|
||||
unsigned count;
|
||||
clang_getOverriddenCursors(data->cursor, &overridden, &count);
|
||||
ret.resize(count);
|
||||
for (unsigned i=0; i<count; ++i)
|
||||
ret[i] = data->ast->create(overridden[i]);
|
||||
clang_disposeOverriddenCursors(overridden);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned argumentCount() const { return data ? RTags::cursorArguments(data->cursor) : 0; }
|
||||
inline Cursors arguments() const;
|
||||
|
||||
int fieldBitWidth() const { return intProperty(&clang_getFieldDeclBitWidth); }
|
||||
CursorType typedefUnderlyingType() const { return cursorTypeProperty(&clang_getTypedefDeclUnderlyingType); }
|
||||
CursorType enumIntegerType() const { return cursorTypeProperty(&clang_getEnumDeclIntegerType); }
|
||||
long long enumConstantValue() const { return intProperty(&clang_getEnumConstantDeclValue); }
|
||||
std::string includedFile() const
|
||||
{
|
||||
if (data && clang_getCursorKind(data->cursor) == CXCursor_InclusionDirective) {
|
||||
CXFile includedFile = clang_getIncludedFile(data->cursor);
|
||||
if (includedFile) {
|
||||
CXStringScope fn = clang_getFileName(includedFile);
|
||||
const char *cstr = clang_getCString(fn);
|
||||
if (cstr)
|
||||
return Path::resolved(cstr).ref();
|
||||
}
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
unsigned templateArgumentCount() const
|
||||
{
|
||||
#if CINDEX_VERSION > CINDEX_VERSION_ENCODE(0, 28)
|
||||
return data ? clang_Cursor_getNumTemplateArguments(data->cursor) : 0;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
CursorType templateArgumentType(unsigned idx) const
|
||||
{
|
||||
#if CINDEX_VERSION > CINDEX_VERSION_ENCODE(0, 28)
|
||||
return data ? CursorType(data->ast, clang_Cursor_getTemplateArgumentType(data->cursor, idx)) : CursorType();
|
||||
#else
|
||||
(void)idx;
|
||||
return CursorType();
|
||||
#endif
|
||||
}
|
||||
long long templateArgumentValue(unsigned idx) const
|
||||
{
|
||||
#if CINDEX_VERSION > CINDEX_VERSION_ENCODE(0, 28)
|
||||
return data ? clang_Cursor_getTemplateArgumentValue(data->cursor, idx) : -1;
|
||||
#else
|
||||
(void)idx;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
std::string templateArgumentKind(unsigned idx) const
|
||||
{
|
||||
std::string ret;
|
||||
#if CINDEX_VERSION > CINDEX_VERSION_ENCODE(0, 28)
|
||||
if (data)
|
||||
ret = toString(clang_Cursor_getTemplateArgumentKind(data->cursor, idx));
|
||||
#else
|
||||
(void)idx;
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
Cursor referenced() const { return cursorProperty(&clang_getCursorReferenced); }
|
||||
Cursor canonical() const { return cursorProperty(&clang_getCanonicalCursor); }
|
||||
Cursor lexicalParent() const { return cursorProperty(&clang_getCursorLexicalParent); }
|
||||
Cursor semanticParent() const { return cursorProperty(&clang_getCursorSemanticParent); }
|
||||
Cursor definitionCursor() const { return cursorProperty(& clang_getCursorDefinition); }
|
||||
Cursor specializedCursorTemplate() const { return cursorProperty(&clang_getSpecializedCursorTemplate); }
|
||||
int childCount() const { return data ? data->children.size() : 0; }
|
||||
Cursor child(int idx) const { return data ? Cursor{ data->children.value(idx)->shared_from_this() } : Cursor(); }
|
||||
Cursors children() const;
|
||||
enum QueryResult {
|
||||
None = 0x0,
|
||||
Add = 0x1,
|
||||
Recurse = 0x2
|
||||
};
|
||||
unsigned none() const { return None; };
|
||||
unsigned add() const { return Add; };
|
||||
unsigned recurse() const { return Recurse; };
|
||||
Cursors query(const std::function<unsigned(const Cursor&)> callback, int depth = INT_MAX) const
|
||||
{
|
||||
Cursors ret;
|
||||
if (data) {
|
||||
const unsigned result = callback(*this);
|
||||
if (result & Add)
|
||||
ret.append(*this);
|
||||
if (result & Recurse && depth > 0) {
|
||||
for (Data *childData : data->children) {
|
||||
const Cursor child = { childData->shared_from_this() };
|
||||
ret.append(child.query(callback, depth - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool isBitField() const { return intProperty<unsigned, bool>(&clang_Cursor_isBitField); }
|
||||
bool isVirtualBase() const { return intProperty<unsigned, bool>(&clang_isVirtualBase); }
|
||||
bool isStatic() const { return intProperty<unsigned, bool>(&clang_CXXMethod_isStatic); }
|
||||
bool isVirtual() const { return intProperty<unsigned, bool>(&clang_CXXMethod_isVirtual); }
|
||||
bool isPureVirtual() const
|
||||
{
|
||||
#if CINDEX_VERSION >= CINDEX_VERSION_ENCODE(0, 20)
|
||||
return intProperty<unsigned, bool>(&clang_CXXMethod_isPureVirtual);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
bool isConst() const
|
||||
{
|
||||
#if CINDEX_VERSION > CINDEX_VERSION_ENCODE(0, 20)
|
||||
return intProperty<unsigned, bool>(&clang_CXXMethod_isConst);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
bool isDefinition() const { return intProperty<unsigned, bool>(&clang_isCursorDefinition); }
|
||||
bool isDynamicCall() const { return intProperty<int, bool>(&clang_Cursor_isDynamicCall); }
|
||||
|
||||
template <typename Func> std::string stringProperty(Func func) const { return data ? toString(func(data->cursor)) : std::string(); }
|
||||
template <typename Type, typename Ret = Type> Ret intProperty(Type (*func)(CXCursor), Type defaultValue = Type()) const { return data ? func(data->cursor) : defaultValue; }
|
||||
template <typename Func> Cursor cursorProperty(Func func) const { return data ? data->ast->create(func(data->cursor)) : Cursor(); }
|
||||
template <typename Func> CursorType cursorTypeProperty(Func func) const { return data ? CursorType(data->ast, func(data->cursor)) : CursorType(); }
|
||||
|
||||
std::shared_ptr<Data> data;
|
||||
};
|
||||
|
||||
struct Cursors : public List<Cursor>
|
||||
{
|
||||
int size() const { return List<Cursor>::size(); }
|
||||
Cursor at(int idx) const { return value(idx); }
|
||||
};
|
||||
|
||||
struct CursorType {
|
||||
CursorType(const AST *a, const CXType &t) : ast(a), type(t) {}
|
||||
CursorType(const AST *a = 0) : ast(a) { memset(&type, 0, sizeof(type)); }
|
||||
|
||||
std::string spelling() const { return toString(clang_getTypeSpelling(type)); }
|
||||
Cursor declaration() const { return ast ? ast->create(clang_getTypeDeclaration(type)) : Cursor(); }
|
||||
std::string callingConvention() const { return toString(clang_getFunctionTypeCallingConv(type)); }
|
||||
std::string referenceType() const
|
||||
{
|
||||
#if CINDEX_VERSION >= CINDEX_VERSION_ENCODE(0, 20)
|
||||
return toString(clang_Type_getCXXRefQualifier(type));
|
||||
#else
|
||||
return std::string();
|
||||
#endif
|
||||
}
|
||||
unsigned argumentCount() const { return clang_getNumArgTypes(type); }
|
||||
CursorType argument(unsigned idx) const { return CursorType(ast, clang_getArgType(type, idx)); }
|
||||
unsigned templateArgumentCount() const
|
||||
{
|
||||
#if CINDEX_VERSION > CINDEX_VERSION_ENCODE(0, 20)
|
||||
return clang_Type_getNumTemplateArguments(type);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
CursorType templateArgument(unsigned idx) const
|
||||
{
|
||||
#if CINDEX_VERSION > CINDEX_VERSION_ENCODE(0, 20)
|
||||
return CursorType(ast, clang_Type_getTemplateArgumentAsType(type, idx));
|
||||
#else
|
||||
(void)idx;
|
||||
return CursorType();
|
||||
#endif
|
||||
}
|
||||
CursorType canonicalType() const { return CursorType(ast, clang_getCanonicalType(type)); }
|
||||
CursorType pointeeType() const { return CursorType(ast, clang_getPointeeType(type)); }
|
||||
CursorType resultType() const { return CursorType(ast, clang_getResultType(type)); }
|
||||
CursorType elementType() const { return CursorType(ast, clang_getElementType(type)); }
|
||||
CursorType arrayElementType() const { return CursorType(ast, clang_getElementType(type)); }
|
||||
CursorType classType() const
|
||||
{
|
||||
#if CINDEX_VERSION >= CINDEX_VERSION_ENCODE(0, 20)
|
||||
return CursorType(ast, clang_Type_getClassType(type));
|
||||
#else
|
||||
return CursorType();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool isConstQualified() const { return clang_isConstQualifiedType(type); }
|
||||
bool isEstrictQualified() const { return clang_isRestrictQualifiedType(type); }
|
||||
bool isVolatileQualified() const { return clang_isVolatileQualifiedType(type); }
|
||||
bool isVariadic() const { return clang_isFunctionTypeVariadic(type); }
|
||||
bool isPod() const { return clang_isPODType(type); }
|
||||
long long numElements() const { return clang_getNumElements(type); }
|
||||
long long arraySize() const { return clang_getArraySize(type); }
|
||||
long long alignOf() const { return clang_Type_getAlignOf(type); }
|
||||
long long sizeOf() const { return clang_Type_getSizeOf(type); }
|
||||
|
||||
const AST *ast;
|
||||
CXType type;
|
||||
};
|
||||
|
||||
struct Diagnostic {
|
||||
|
||||
|
||||
};
|
||||
|
||||
struct SkippedRange {
|
||||
|
||||
|
||||
};
|
||||
|
||||
static std::shared_ptr<AST> create(const Source &source, const String &sourceCode, CXTranslationUnit unit);
|
||||
List<String> evaluate(const String &script);
|
||||
Cursor *root() const { return mRoot; }
|
||||
List<Diagnostic> diagnostics() const;
|
||||
List<SkippedRange> skippedRanges() const;
|
||||
static SourceLocation createLocation(const CXCursor &cursor) { return createLocation(clang_getCursorLocation(cursor)); }
|
||||
static SourceLocation createLocation(const CXSourceLocation &location)
|
||||
{
|
||||
SourceLocation loc;
|
||||
loc.location = RTags::createLocation(location, &loc.offset);
|
||||
return loc;
|
||||
}
|
||||
|
||||
Cursor create(const CXCursor &cursor) const
|
||||
{
|
||||
if (clang_isInvalid(clang_getCursorKind(cursor)))
|
||||
return Cursor();
|
||||
|
||||
auto match = [&cursor](const Cursors &cursors) -> Cursor {
|
||||
for (const Cursor &c : cursors) {
|
||||
assert(c.data);
|
||||
if (clang_equalCursors(c.data->cursor, cursor)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
// The explicit const cast is here to satisfy travis clang matrix.
|
||||
// clang 3.4 does not allow to use two different return types
|
||||
return Cursor();
|
||||
};
|
||||
|
||||
const std::string usr = toString(clang_getCursorUSR(cursor));
|
||||
if (!usr.empty()) {
|
||||
auto it = mByUsr.find(usr);
|
||||
if (it != mByUsr.end()) {
|
||||
const Cursor ret = match(it->second);
|
||||
if (ret.data)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
const SourceLocation loc = createLocation(cursor);
|
||||
if (!loc.isNull()) {
|
||||
auto it = mByLocation.find(loc);
|
||||
if (it != mByLocation.end()) {
|
||||
const Cursor ret = match(it->second);
|
||||
if (ret.data)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return construct(cursor, 0, loc, usr);
|
||||
}
|
||||
private:
|
||||
static CXChildVisitResult visitor(CXCursor cursor, CXCursor, CXClientData u);
|
||||
Cursor construct(const CXCursor &cursor,
|
||||
Cursor::Data *parent = 0,
|
||||
SourceLocation loc = SourceLocation(),
|
||||
std::string usr = std::string()) const
|
||||
{
|
||||
Cursor ret;
|
||||
if (loc.isNull())
|
||||
loc = createLocation(cursor);
|
||||
if (usr.empty())
|
||||
usr = toString(clang_getCursorUSR(cursor));
|
||||
ret.data.reset(new Cursor::Data(const_cast<AST*>(this), parent, cursor, loc, usr));
|
||||
if (!loc.isNull())
|
||||
mByLocation[loc].append(ret);
|
||||
if (!ret.data->usr.empty())
|
||||
mByUsr[usr].append(ret);
|
||||
if (parent)
|
||||
parent->children.append(ret.data.get());
|
||||
return ret;
|
||||
}
|
||||
AST()
|
||||
: mRoot(0)
|
||||
{}
|
||||
mutable Hash<std::string, Cursors> mByUsr;
|
||||
mutable Map<SourceLocation, Cursors> mByLocation;
|
||||
String mSourceCode;
|
||||
List<String> mReturnValues;
|
||||
Cursor *mRoot;
|
||||
std::shared_ptr<sel::State> mState;
|
||||
};
|
||||
|
||||
|
||||
inline AST::Cursors AST::Cursor::children() const
|
||||
{
|
||||
Cursors ret;
|
||||
if (data) {
|
||||
ret.resize(data->children.size());
|
||||
int i = 0;
|
||||
for (Data *child : data->children) {
|
||||
ret[i++] = Cursor { child->shared_from_this() };
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline AST::Cursors AST::Cursor::arguments() const
|
||||
{
|
||||
AST::Cursors ret;
|
||||
if (data) {
|
||||
List<CXCursor> args;
|
||||
const unsigned size = RTags::cursorArguments(data->cursor, &args);
|
||||
if (size) {
|
||||
ret.resize(size);
|
||||
for (unsigned i=0; i<size; ++i) {
|
||||
ret[i] = data->ast->create(args[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
@@ -1,364 +0,0 @@
|
||||
set(CMAKE_SKIP_BUILD_RPATH FALSE)
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
|
||||
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
|
||||
include(ExternalProject)
|
||||
|
||||
if (RTAGS_BUILD_CLANG)
|
||||
set(LIBCLANG_VERSION_STRING "5.0.0")
|
||||
if (RTAGS_NO_INSTALL)
|
||||
set(CLANG_INSTALL_PATH ${CMAKE_CURRENT_BINARY_DIR}/llvmclang-prefix/install)
|
||||
else ()
|
||||
set(CLANG_INSTALL_PATH ${CMAKE_INSTALL_PREFIX}/rtags-llvmclang)
|
||||
endif ()
|
||||
ExternalProject_Add(llvmclang
|
||||
DOWNLOAD_COMMAND ${PROJECT_SOURCE_DIR}/scripts/getclang.sh "${LIBCLANG_VERSION_STRING}"
|
||||
SOURCE_DIR llvmclang-prefix/src/llvm
|
||||
CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${CLANG_INSTALL_PATH} -DLLVM_BUILD_TOOLS=1
|
||||
LOG_DOWNLOAD 1 # Wrap download in script to log output
|
||||
LOG_UPDATE 1 # Wrap update in script to log output
|
||||
LOG_CONFIGURE 1 # Wrap configure in script to log output
|
||||
LOG_BUILD 1 # Wrap build in script to log output
|
||||
LOG_TEST 1 # Wrap test in script to log output
|
||||
LOG_INSTALL 1 # Wrap install in script to log output
|
||||
)
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
|
||||
set(CLANG_LIBRARY_NAME "libclang.dylib")
|
||||
add_definitions("-DCLANG_INCLUDE=${CLANG_INSTALL_PATH}/include/c++/v1") # add libcxx include
|
||||
else ()
|
||||
set(CLANG_LIBRARY_NAME "libclang.so")
|
||||
endif ()
|
||||
set(LIBCLANG_LIBRARIES ${CLANG_INSTALL_PATH}/lib/${CLANG_LIBRARY_NAME})
|
||||
set(LIBCLANG_LIBDIR ${CLANG_INSTALL_PATH}/lib)
|
||||
set(LIBCLANG_CXXFLAGS "-I${CLANG_INSTALL_PATH}/include")
|
||||
endif ()
|
||||
|
||||
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/RTagsVersion.h.in" "${CMAKE_CURRENT_BINARY_DIR}/include/RTagsVersion.h")
|
||||
|
||||
# Treat MSYS as Cygwin
|
||||
if (MSYS)
|
||||
set(CYGWIN 1)
|
||||
set(CMAKE_SYSTEM_NAME "CYGWIN")
|
||||
endif ()
|
||||
|
||||
# Set RPATH when installing to a custom (non-system) directory
|
||||
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir)
|
||||
if ("${isSystemDir}" STREQUAL "-1")
|
||||
list(APPEND CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib)
|
||||
endif ()
|
||||
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/EnsureLibraries.cmake)
|
||||
|
||||
if (NOT DEFINED LIBCLANG_LIBRARIES)
|
||||
set(LIBCLANG_LIBRARIES $ENV{LIBCLANG_LIBRARIES})
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED LIBCLANG_CXXFLAGS)
|
||||
set(LIBCLANG_CXXFLAGS $ENV{LIBCLANG_CXXFLAGS})
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED LIBCLANG_LIBDIR)
|
||||
set(LIBCLANG_LIBDIR $ENV{LIBCLANG_LIBDIR})
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED LIBCLANG_LIBRARIES OR NOT DEFINED LIBCLANG_CXXFLAGS OR NOT DEFINED LIBCLANG_LIBDIR)
|
||||
find_package(LibClang REQUIRED)
|
||||
endif ()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LIBCLANG_CXXFLAGS}")
|
||||
|
||||
if (NOT RTAGS_BUILD_CLANG)
|
||||
set(LIBCLANG_COMPILE_TEST "
|
||||
#include <clang-c/Index.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
CXIndex index = clang_createIndex(0, true);
|
||||
CXTranslationUnit unit = clang_parseTranslationUnit(index, 0, 0, 0, 0, 0, 0);
|
||||
clang_disposeTranslationUnit(unit);
|
||||
clang_disposeIndex(index);
|
||||
return 0;
|
||||
}
|
||||
")
|
||||
include(CheckCXXSourceCompiles)
|
||||
set(CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} ${CMAKE_CXX_FLAGS} ${LIBCLANG_CXXFLAGS})
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${LIBCLANG_LIBRARIES})
|
||||
check_cxx_source_compiles("${LIBCLANG_COMPILE_TEST}" LIBCLANG_COMPILES)
|
||||
if (NOT LIBCLANG_COMPILES)
|
||||
message(FATAL_ERROR "Failed to compile small clang test app.\nIt's likely that the include file <clang-c/Index.h> could not be found!\nMaybe you need to install the clang development package?\nSee CMakeFiles/CMakeError.log for more info.")
|
||||
endif ()
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
unset(CMAKE_REQUIRED_LIBRARIES)
|
||||
unset(LIBCLANG_COMPILES)
|
||||
endif ()
|
||||
|
||||
set(CXX_11_EXTENDED_REGEX_TEST "
|
||||
#include <regex>
|
||||
|
||||
int main()
|
||||
{
|
||||
try {
|
||||
std::regex rx(\"^(.*):([0-9]+):([0-9]+):?-:?([0-9]+):([0-9]+):?(@[A-Za-z,]+)?\", std::regex_constants::extended);
|
||||
} catch (std::regex_error& e) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
")
|
||||
include(CheckCXXSourceRuns)
|
||||
set(CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} "-std=c++11" ${CMAKE_CXX_FLAGS} ${LIBCLANG_CXXFLAGS})
|
||||
check_cxx_source_runs("${CXX_11_EXTENDED_REGEX_TEST}" CXX_11_EXTENDED_REGEX_SUPPORT)
|
||||
if (NOT CXX_11_EXTENDED_REGEX_SUPPORT)
|
||||
message(FATAL_ERROR "Compiler with complete C++11 support required ( >= GCC 4.9 or >= Clang 3.3)")
|
||||
endif ()
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
unset(CMAKE_REQUIRED_LIBRARIES)
|
||||
unset(CXX_11_EXTENDED_REGEX_SUPPORT)
|
||||
|
||||
if (EXISTS ${CLANG_COMPILATION_INCLUDE})
|
||||
add_definitions(-DHAVE_CXCOMPILATIONDATABASE)
|
||||
endif ()
|
||||
|
||||
if (LUA_ENABLED)
|
||||
ExternalProject_Add(lua
|
||||
DOWNLOAD_COMMAND ""
|
||||
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/lua
|
||||
INSTALL_COMMAND ""
|
||||
CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=0 -DCMAKE_GENERATOR=${CMAKE_GENERATOR})
|
||||
endif ()
|
||||
|
||||
set(RCT_RTTI_ENABLED 1)
|
||||
set(RCT_NO_INSTALL 1)
|
||||
set(RCT_NO_LIBRARY 1)
|
||||
# Everyting which as been set either in rct/rct.cmake or rct/compiler.cmake
|
||||
# doesn't need to be set in this file again.
|
||||
include(rct/rct.cmake)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wstrict-aliasing=2 -Wcast-qual -fPIC")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstrict-aliasing=2 -Wcast-qual -fPIC")
|
||||
if (NOT CYGWIN)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-all -Wstack-protector")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-all -Wstack-protector")
|
||||
endif ()
|
||||
|
||||
if (CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||
add_compile_options("-Og")
|
||||
add_definitions("-DRTAGS_DEBUG")
|
||||
set(RCT_EVENTLOOP_CALLBACK_TIME_THRESHOLD 2000)
|
||||
endif ()
|
||||
|
||||
add_definitions(
|
||||
"-DRTAGS_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
"-DCLANG_LIBDIR=${LIBCLANG_LIBDIR}"
|
||||
"-DCLANG_VERSION=${LIBCLANG_VERSION_STRING}"
|
||||
"-DOS_${CMAKE_SYSTEM_NAME}"
|
||||
${RCT_DEFINITIONS})
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
add_definitions(-D__LONG_LONG_SUPPORTED)
|
||||
endif ()
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
|
||||
set(RTAGS_SOURCES
|
||||
ClangIndexer.cpp
|
||||
ClangThread.cpp
|
||||
ClassHierarchyJob.cpp
|
||||
CompilerManager.cpp
|
||||
CompletionThread.cpp
|
||||
DependenciesJob.cpp
|
||||
FileManager.cpp
|
||||
FindFileJob.cpp
|
||||
FindSymbolsJob.cpp
|
||||
FollowLocationJob.cpp
|
||||
IncludeFileJob.cpp
|
||||
IndexMessage.cpp
|
||||
IndexParseData.cpp
|
||||
IndexerJob.cpp
|
||||
JobScheduler.cpp
|
||||
ListSymbolsJob.cpp
|
||||
Location.cpp
|
||||
Preprocessor.cpp
|
||||
ProcThread.cpp
|
||||
Project.cpp
|
||||
QueryJob.cpp
|
||||
QueryMessage.cpp
|
||||
RClient.cpp
|
||||
RTags.cpp
|
||||
ReferencesJob.cpp
|
||||
Sandbox.cpp
|
||||
ScanThread.cpp
|
||||
Server.cpp
|
||||
Source.cpp
|
||||
StatusJob.cpp
|
||||
Symbol.cpp
|
||||
Symbol.cpp
|
||||
SymbolInfoJob.cpp
|
||||
Token.cpp
|
||||
TokensJob.cpp
|
||||
${RCT_SOURCES})
|
||||
|
||||
if (LUA_ENABLED)
|
||||
list(APPEND RTAGS_SOURCES AST.cpp)
|
||||
endif ()
|
||||
|
||||
add_library(rtags STATIC ${RTAGS_SOURCES})
|
||||
if (RTAGS_BUILD_CLANG)
|
||||
add_dependencies(rtags llvmclang)
|
||||
endif ()
|
||||
if (LUA_ENABLED)
|
||||
add_dependencies(rtags lua)
|
||||
endif ()
|
||||
|
||||
if (RTAGS_COTIRE)
|
||||
include(cotire)
|
||||
set_target_properties(rtags PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE)
|
||||
cotire(rtags)
|
||||
endif ()
|
||||
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
${RCT_INCLUDE_DIRS}
|
||||
${CMAKE_CURRENT_LIST_DIR}/selene/include
|
||||
${CMAKE_CURRENT_BINARY_DIR}/lua-prefix/src/lua-build
|
||||
${CMAKE_CURRENT_LIST_DIR}/lua/src)
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
|
||||
set(START_GROUP "")
|
||||
set(END_GROUP "")
|
||||
else ()
|
||||
set(START_GROUP "-Wl,--start-group")
|
||||
set(END_GROUP "-Wl,--end-group")
|
||||
endif ()
|
||||
|
||||
set(RTAGS_LIBRARIES rtags ${START_GROUP} ${LIBCLANG_LIBRARIES} ${END_GROUP})
|
||||
if (LUA_ENABLED)
|
||||
list(APPEND RTAGS_LIBRARIES -L${CMAKE_CURRENT_BINARY_DIR}/lua-prefix/src/lua-build/ liblua.a)
|
||||
add_definitions(-DRTAGS_HAS_LUA)
|
||||
endif ()
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
set_source_files_properties(AST.cpp PROPERTIES
|
||||
COMPILE_FLAGS "-Wno-unused-parameter -Wno-unused-variable -ftemplate-depth=2000 -Wno-cast-qual -Wno-shadow")
|
||||
else ()
|
||||
set_source_files_properties(AST.cpp PROPERTIES
|
||||
COMPILE_FLAGS "-Wno-unused-but-set-parameter -Wno-unused-parameter -Wno-unused-variable -ftemplate-depth=2000 -Wno-cast-qual -Wno-shadow")
|
||||
endif()
|
||||
|
||||
# RCT_LIBRARIES and stdc++ library must be at the end
|
||||
set(RTAGS_LIBRARIES ${RTAGS_LIBRARIES} -lstdc++ ${RCT_LIBRARIES})
|
||||
add_executable(rc rc.cpp)
|
||||
target_link_libraries(rc ${RTAGS_LIBRARIES})
|
||||
|
||||
add_executable(rdm rdm.cpp)
|
||||
target_link_libraries(rdm ${RTAGS_LIBRARIES})
|
||||
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
|
||||
add_executable(rp rp.cpp)
|
||||
target_link_libraries(rp ${RTAGS_LIBRARIES})
|
||||
|
||||
if (CYGWIN)
|
||||
EnsureLibraries(rdm rct)
|
||||
endif ()
|
||||
|
||||
if (EMACS)
|
||||
set(EMACS_EXECUTABLE ${EMACS})
|
||||
else ()
|
||||
find_program(EMACS_EXECUTABLE emacs)
|
||||
endif ()
|
||||
|
||||
if (EMACS_EXECUTABLE)
|
||||
execute_process(COMMAND ${EMACS_EXECUTABLE} --version
|
||||
RESULT_VARIABLE EMACS_ERROR
|
||||
OUTPUT_VARIABLE EMACS_VERSION_INFORMATION
|
||||
ERROR_QUIET)
|
||||
if (NOT EMACS_ERROR)
|
||||
string(REGEX MATCH "[0-9]+(\\.[0-9]+)*" EMACS_VERSION ${EMACS_VERSION_INFORMATION})
|
||||
if ("${EMACS_VERSION}" LESS "24.3")
|
||||
message(WARNING "Installed emacs version is to old, elisp files are not going to be installed, minimum required version is >=24.3!")
|
||||
set(RTAGS_NO_ELISP_FILES TRUE)
|
||||
else()
|
||||
message(STATUS "Found emacs version ${EMACS_VERSION}")
|
||||
endif ()
|
||||
else ()
|
||||
message(STATUS "Could not get emacs version (\"emacs --version\"), elisp files are not going to be installed")
|
||||
set(RTAGS_NO_ELISP_FILES TRUE)
|
||||
endif ()
|
||||
else ()
|
||||
set(RTAGS_NO_ELISP_FILES TRUE)
|
||||
message(STATUS "Emacs was not found, elisp files are not going to be installed!")
|
||||
endif ()
|
||||
|
||||
set(RTAGS_ELISP_FILES
|
||||
rtags.el
|
||||
ac-rtags.el
|
||||
helm-rtags.el
|
||||
ivy-rtags.el
|
||||
company-rtags.el
|
||||
flycheck-rtags.el)
|
||||
|
||||
if (LUA_ENABLED AND NOT RTAGS_NO_LUA_FILES AND LUA_FOUND AND NOT ${CMAKE_CURRENT_BINARY_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/rtags.lua
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/rtags.lua ${CMAKE_CURRENT_BINARY_DIR}/rtags.lua
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/rtags.lua
|
||||
COMMENT "Copying ${CMAKE_CURRENT_SOURCE_DIR}/rtags.lua -> ${CMAKE_CURRENT_BINARY_DIR}/rtags.lua")
|
||||
endif ()
|
||||
|
||||
if (NOT RTAGS_NO_ELISP_FILES)
|
||||
if (NOT RTAGS_ELISP_INSTALL_LOCATION)
|
||||
set(RTAGS_ELISP_INSTALL_LOCATION ${CMAKE_INSTALL_PREFIX}/share/emacs/site-lisp/rtags/)
|
||||
endif ()
|
||||
set(RTAGS_ELISP_SOURCES "")
|
||||
foreach(el ${RTAGS_ELISP_FILES})
|
||||
if (NOT ${CMAKE_CURRENT_BINARY_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${el}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/${el} ${CMAKE_CURRENT_BINARY_DIR}/${el}
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${el}
|
||||
COMMENT "Copying ${CMAKE_CURRENT_SOURCE_DIR}/${el} -> ${CMAKE_CURRENT_BINARY_DIR}/${el}")
|
||||
endif ()
|
||||
list(APPEND RTAGS_ELISP_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/${el})
|
||||
if (NOT RTAGS_NO_ELISP_BYTECOMPILE)
|
||||
list(APPEND RTAGS_ELISP_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/${el}c)
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${el}c
|
||||
COMMAND ${EMACS_EXECUTABLE} -batch -l ${CMAKE_CURRENT_SOURCE_DIR}/compile-shim.elisp -l ${CMAKE_CURRENT_SOURCE_DIR}/rtags.el -f batch-byte-compile
|
||||
${CMAKE_CURRENT_BINARY_DIR}/${el}
|
||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${el}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Creating byte-compiled Emacs lisp ${CMAKE_CURRENT_BINARY_DIR}/${el}c")
|
||||
add_custom_target(emacs_byte_compile_${el} ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${el}c)
|
||||
endif ()
|
||||
endforeach()
|
||||
endif ()
|
||||
|
||||
install(CODE "message(\"Installing rtags...\")")
|
||||
include(BashCompletion)
|
||||
if (BASH_COMPLETION_FOUND)
|
||||
file (MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/completions")
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink rtags rc
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink rtags rdm
|
||||
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/completions")
|
||||
install(FILES rtags-bash-completion.bash DESTINATION ${BASH_COMPLETION_COMPLETIONSDIR}
|
||||
PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ
|
||||
RENAME rtags)
|
||||
install(FILES "${PROJECT_BINARY_DIR}/completions/rc" "${PROJECT_BINARY_DIR}/completions/rdm"
|
||||
DESTINATION ${BASH_COMPLETION_COMPLETIONSDIR})
|
||||
endif ()
|
||||
install(TARGETS rdm rc rp RUNTIME DESTINATION bin COMPONENT rtags)
|
||||
install(FILES ../bin/gcc-rtags-wrapper.sh DESTINATION bin
|
||||
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||
install(FILES ../man/man7/rc.7 ../man/man7/rdm.7 DESTINATION share/man/man7/)
|
||||
|
||||
if (UNIX AND NOT ${SKIP_CTEST})
|
||||
install(CODE "execute_process(COMMAND ctest)")
|
||||
endif ()
|
||||
|
||||
if (NOT RTAGS_NO_ELISP_FILES)
|
||||
install(FILES ${RTAGS_ELISP_SOURCES} DESTINATION ${RTAGS_ELISP_INSTALL_LOCATION})
|
||||
endif ()
|
||||
|
||||
if (CLANGTEST_ENABLED)
|
||||
add_executable(clangtest clangtest.cpp)
|
||||
target_link_libraries(clangtest ${LIBCLANG_LIBRARIES})
|
||||
endif ()
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,196 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef ClangIndexer_h
|
||||
#define ClangIndexer_h
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include "Token.h"
|
||||
|
||||
#include "IndexDataMessage.h"
|
||||
#include "rct/Hash.h"
|
||||
#include "rct/Path.h"
|
||||
#include "rct/StopWatch.h"
|
||||
#include "RTags.h"
|
||||
#include "Server.h"
|
||||
#include "Symbol.h"
|
||||
#include <unordered_set>
|
||||
|
||||
struct Unit;
|
||||
class ClangIndexer : public RTags::DiagnosticsProvider
|
||||
{
|
||||
public:
|
||||
ClangIndexer();
|
||||
~ClangIndexer();
|
||||
|
||||
bool exec(const String &data);
|
||||
static Flags<Server::Option> serverOpts() { return sServerOpts; }
|
||||
static const Path &serverSandboxRoot() { return sServerSandboxRoot; }
|
||||
private:
|
||||
bool diagnose();
|
||||
bool visit();
|
||||
bool parse();
|
||||
void tokenize(CXFile file, uint32_t fileId, const Path &path);
|
||||
bool writeFiles(const Path &root, String &error);
|
||||
|
||||
void addFileSymbol(uint32_t file);
|
||||
int symbolLength(CXCursorKind kind, const CXCursor &cursor);
|
||||
void extractArguments(List<Symbol::Argument> *arguments, const CXCursor &cursor);
|
||||
CXCursor resolveTemplate(CXCursor cursor, Location location = Location(), bool *specialized = 0);
|
||||
static CXCursor resolveTypedef(CXCursor cursor);
|
||||
|
||||
// DiagnosticsProvider
|
||||
using RTags::DiagnosticsProvider::createLocation;
|
||||
virtual Location createLocation(const Path &file, unsigned int line, unsigned int col, bool *blocked = 0) override;
|
||||
virtual CXTranslationUnit unit(size_t u) const override;
|
||||
virtual size_t unitCount() const override
|
||||
{
|
||||
return mTranslationUnits.size();
|
||||
}
|
||||
virtual size_t diagnosticCount(size_t unit) const override
|
||||
{
|
||||
if (CXTranslationUnit u = mTranslationUnits[unit]->unit) {
|
||||
return clang_getNumDiagnostics(u);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
virtual CXDiagnostic diagnostic(size_t unit, size_t idx) const override
|
||||
{
|
||||
assert(mTranslationUnits[unit]->unit);
|
||||
return clang_getDiagnostic(mTranslationUnits[unit]->unit, idx);
|
||||
}
|
||||
|
||||
virtual uint32_t sourceFileId() const override { return mSources.front().fileId; }
|
||||
virtual IndexDataMessage &indexDataMessage() override { return mIndexDataMessage; }
|
||||
|
||||
String addNamePermutations(const CXCursor &cursor,
|
||||
Location location,
|
||||
RTags::CursorType cursorType);
|
||||
|
||||
CXChildVisitResult handleCursor(const CXCursor &cursor, CXCursorKind kind,
|
||||
Location location, Symbol **cursorPtr = 0);
|
||||
bool handleReference(const CXCursor &cursor, CXCursorKind kind,
|
||||
Location loc, CXCursor reference,
|
||||
Symbol **cursorPtr = 0);
|
||||
void handleBaseClassSpecifier(const CXCursor &cursor);
|
||||
void handleInclude(const CXCursor &cursor, CXCursorKind kind, Location location);
|
||||
void handleLiteral(const CXCursor &cursor, CXCursorKind kind, Location location);
|
||||
CXChildVisitResult handleStatement(const CXCursor &cursor, CXCursorKind kind, Location location);
|
||||
Location findByUSR(const CXCursor &cursor, CXCursorKind kind, Location loc) const;
|
||||
std::unordered_set<CXCursor> addOverriddenCursors(const CXCursor &cursor, Location location);
|
||||
bool superclassTemplateMemberFunctionUgleHack(const CXCursor &cursor, CXCursorKind kind,
|
||||
Location location, const CXCursor &ref,
|
||||
Symbol **cursorPtr = 0);
|
||||
void visit(CXCursor cursor)
|
||||
{
|
||||
mParents.append(cursor);
|
||||
clang_visitChildren(cursor, visitorHelper, this);
|
||||
mParents.removeLast();
|
||||
}
|
||||
CXChildVisitResult indexVisitor(CXCursor cursor);
|
||||
static CXChildVisitResult visitorHelper(CXCursor cursor, CXCursor, CXClientData userData);
|
||||
static CXChildVisitResult verboseVisitor(CXCursor cursor, CXCursor, CXClientData userData);
|
||||
static CXChildVisitResult resolveAutoTypeRefVisitor(CXCursor cursor, CXCursor, CXClientData data);
|
||||
|
||||
void onMessage(const std::shared_ptr<Message> &msg, const std::shared_ptr<Connection> &conn);
|
||||
|
||||
struct Unit {
|
||||
Map<Location, Symbol> symbols;
|
||||
Map<Location, Map<String, uint16_t> > targets;
|
||||
Map<String, Set<Location> > usrs;
|
||||
Map<String, Set<Location> > symbolNames;
|
||||
Map<uint32_t, Token> tokens;
|
||||
};
|
||||
|
||||
std::shared_ptr<Unit> &unit(uint32_t fileId)
|
||||
{
|
||||
std::shared_ptr<Unit> &unit = mUnits[fileId];
|
||||
if (!unit) {
|
||||
unit.reset(new Unit);
|
||||
}
|
||||
return unit;
|
||||
}
|
||||
std::shared_ptr<Unit> unit(Location loc) { return unit(loc.fileId()); }
|
||||
|
||||
enum FindResult {
|
||||
Found,
|
||||
NotIndexed,
|
||||
NotFound
|
||||
};
|
||||
Symbol findSymbol(Location location, FindResult *result) const;
|
||||
|
||||
struct MacroLocationData {
|
||||
Set<size_t> arguments;
|
||||
List<Location> locations;
|
||||
};
|
||||
struct MacroData {
|
||||
List<String> arguments;
|
||||
Map<String, MacroLocationData> data;
|
||||
};
|
||||
Map<Location, MacroData> mMacroTokens;
|
||||
|
||||
Hash<uint32_t, std::shared_ptr<Unit> > mUnits;
|
||||
|
||||
Path mProject;
|
||||
SourceList mSources;
|
||||
Path mSourceFile;
|
||||
IndexDataMessage mIndexDataMessage;
|
||||
List<std::shared_ptr<RTags::TranslationUnit> > mTranslationUnits;
|
||||
size_t mCurrentTranslationUnit;
|
||||
CXCursor mLastCursor;
|
||||
Symbol *mLastCallExprSymbol;
|
||||
Location mLastClass;
|
||||
uint32_t mVisitFileResponseMessageFileId;
|
||||
bool mVisitFileResponseMessageVisit;
|
||||
Path mSocketFile;
|
||||
StopWatch mTimer;
|
||||
int mParseDuration, mVisitDuration, mBlocked, mAllowed,
|
||||
mIndexed, mVisitFileTimeout, mIndexDataMessageTimeout,
|
||||
mFileIdsQueried, mFileIdsQueriedTime, mCursorsVisited;
|
||||
UnsavedFiles mUnsavedFiles;
|
||||
List<String> mDebugLocations;
|
||||
FILE *mLogFile;
|
||||
std::shared_ptr<Connection> mConnection;
|
||||
Path mDataDir;
|
||||
bool mUnionRecursion;
|
||||
|
||||
struct Scope {
|
||||
enum ScopeType {
|
||||
FunctionDefinition,
|
||||
FunctionDeclaration,
|
||||
Other
|
||||
};
|
||||
ScopeType type;
|
||||
Symbol *symbol;
|
||||
Location start, end;
|
||||
};
|
||||
List<Scope> mScopeStack;
|
||||
|
||||
struct Loop { // or switch
|
||||
CXCursorKind kind;
|
||||
Location start, end;
|
||||
};
|
||||
List<Loop> mLoopStack;
|
||||
|
||||
std::shared_ptr<RTags::TranslationUnit> mSerializeTU;
|
||||
List<CXCursor> mParents;
|
||||
std::unordered_set<CXCursor> mTemplateSpecializations;
|
||||
size_t mInTemplateFunction;
|
||||
|
||||
static Flags<Server::Option> sServerOpts;
|
||||
static Path sServerSandboxRoot;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,334 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "ClangThread.h"
|
||||
#include "rct/Connection.h"
|
||||
#include "RTags.h"
|
||||
#include "Server.h"
|
||||
#ifdef RTAGS_HAS_LUA
|
||||
#include "AST.h"
|
||||
#endif
|
||||
|
||||
struct Dep : public DependencyNode
|
||||
{
|
||||
Dep(uint32_t f)
|
||||
: DependencyNode(f)
|
||||
{}
|
||||
Hash<uint32_t, Map<Location, Location> > references;
|
||||
};
|
||||
|
||||
ClangThread::ClangThread(const std::shared_ptr<QueryMessage> &queryMessage,
|
||||
const Source &source, const std::shared_ptr<Connection> &conn)
|
||||
: Thread(), mQueryMessage(queryMessage), mSource(source),
|
||||
mConnection(conn), mIndentLevel(0), mAborted(false)
|
||||
{
|
||||
setAutoDelete(true);
|
||||
}
|
||||
|
||||
ClangThread::~ClangThread()
|
||||
{
|
||||
}
|
||||
|
||||
CXChildVisitResult ClangThread::visitor(CXCursor cursor, CXCursor, CXClientData userData)
|
||||
{
|
||||
ClangThread *that = reinterpret_cast<ClangThread*>(userData);
|
||||
assert(that);
|
||||
return that->visit(cursor);
|
||||
}
|
||||
|
||||
CXChildVisitResult ClangThread::visit(const CXCursor &cursor)
|
||||
{
|
||||
if (isAborted())
|
||||
return CXChildVisit_Break;
|
||||
const Location location = RTags::createLocation(cursor);
|
||||
if (!location.isNull()) {
|
||||
if (mQueryMessage->flags() & QueryMessage::DumpCheckIncludes) {
|
||||
checkIncludes(location, cursor);
|
||||
} else {
|
||||
Flags<Location::ToStringFlag> locationFlags;
|
||||
if (mQueryMessage->flags() & QueryMessage::NoColor)
|
||||
locationFlags |= Location::NoColor;
|
||||
|
||||
CXSourceRange range = clang_getCursorExtent(cursor);
|
||||
CXSourceLocation rangeEnd = clang_getRangeEnd(range);
|
||||
unsigned int endLine, endColumn;
|
||||
clang_getPresumedLocation(rangeEnd, 0, &endLine, &endColumn);
|
||||
if (!(mQueryMessage->flags() & QueryMessage::DumpIncludeHeaders) && location.fileId() != mSource.fileId) {
|
||||
return CXChildVisit_Continue;
|
||||
}
|
||||
|
||||
String message;
|
||||
message.reserve(256);
|
||||
|
||||
if (!(mQueryMessage->flags() & QueryMessage::NoContext)) {
|
||||
message = location.context(locationFlags, &mContextCache);
|
||||
}
|
||||
|
||||
if (endLine == location.line()) {
|
||||
message += String::format<32>(" // %d-%d, %d: ", location.column(), endColumn, mIndentLevel);
|
||||
} else {
|
||||
message += String::format<32>(" // %d-%d:%d, %d: ", location.column(), endLine, endColumn, mIndentLevel);
|
||||
}
|
||||
message += RTags::cursorToString(cursor, RTags::AllCursorToStringFlags);
|
||||
message.append(" " + RTags::typeName(cursor));;
|
||||
if (clang_getCursorKind(cursor) == CXCursor_VarDecl) {
|
||||
RTags::Auto autoResolved;
|
||||
if (RTags::resolveAuto(cursor, &autoResolved) && RTags::isValid(autoResolved.cursor)) {
|
||||
message += "auto resolves to " + RTags::cursorToString(autoResolved.cursor, RTags::AllCursorToStringFlags);
|
||||
}
|
||||
}
|
||||
auto printCursor = [&message](const CXCursor &c, bool *spec = 0) {
|
||||
CXCursor canonical = clang_getCanonicalCursor(c);
|
||||
if (canonical != c && RTags::isValid(canonical)) {
|
||||
message.append(" canonical ");
|
||||
message.append(RTags::cursorToString(canonical, RTags::AllCursorToStringFlags));
|
||||
}
|
||||
|
||||
CXCursor specialized = clang_getSpecializedCursorTemplate(c);
|
||||
if (specialized != c && RTags::isValid(specialized)) {
|
||||
message.append(" specialized ");
|
||||
message.append(RTags::cursorToString(specialized, RTags::AllCursorToStringFlags));
|
||||
if (spec)
|
||||
*spec = true;
|
||||
} else if (spec) {
|
||||
*spec = false;
|
||||
}
|
||||
};
|
||||
|
||||
CXCursor ref = clang_getCursorReferenced(cursor);
|
||||
bool refSpecialized = false;
|
||||
if (RTags::isValid(ref) && ref != cursor) {
|
||||
message.append("refs ");
|
||||
message.append(RTags::cursorToString(ref, RTags::AllCursorToStringFlags));
|
||||
printCursor(ref, &refSpecialized);
|
||||
if (refSpecialized && cursor != CXCursor_DeclRefExpr && cursor != CXCursor_MemberRefExpr)
|
||||
refSpecialized = false;
|
||||
}
|
||||
|
||||
printCursor(cursor);
|
||||
|
||||
writeToConnetion(message);
|
||||
if (refSpecialized && false) {
|
||||
visit(ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
++mIndentLevel;
|
||||
clang_visitChildren(cursor, ClangThread::visitor, this);
|
||||
if (isAborted())
|
||||
return CXChildVisit_Break;
|
||||
--mIndentLevel;
|
||||
return CXChildVisit_Continue;
|
||||
}
|
||||
|
||||
void ClangThread::run()
|
||||
{
|
||||
StopWatch sw;
|
||||
const auto key = mConnection->disconnected().connect([this](const std::shared_ptr<Connection> &) { abort(); });
|
||||
|
||||
String sourceCode = mSource.sourceFile().readAll();
|
||||
CXUnsavedFile unsaved = {
|
||||
mSource.sourceFile().constData(),
|
||||
sourceCode.constData(),
|
||||
static_cast<unsigned long>(sourceCode.size())
|
||||
};
|
||||
|
||||
|
||||
std::shared_ptr<RTags::TranslationUnit> translationUnit = RTags::TranslationUnit::create(mSource.sourceFile(),
|
||||
mSource.toCommandLine(Source::Default),
|
||||
&unsaved, 1, CXTranslationUnit_DetailedPreprocessingRecord,
|
||||
false);
|
||||
|
||||
const unsigned long long parseTime = sw.restart();
|
||||
warning() << "parseTime" << parseTime;
|
||||
#ifdef RTAGS_HAS_LUA
|
||||
if (mQueryMessage->type() == QueryMessage::VisitAST) {
|
||||
std::shared_ptr<AST> ast = AST::create(mSource, sourceCode, translationUnit->unit);
|
||||
if (ast) {
|
||||
for (const String script : mQueryMessage->visitASTScripts()) {
|
||||
warning() << "evaluating script:\n" << script;
|
||||
for (const String &str : ast->evaluate(script)) {
|
||||
if (!str.isEmpty()) {
|
||||
writeToConnetion(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (mQueryMessage->type() == QueryMessage::DumpFile && mQueryMessage->flags() & QueryMessage::DumpCheckIncludes)
|
||||
writeToConnetion(String::format<128>("Indexed: %s => %s", translationUnit->clangLine.constData(), translationUnit ? "success" : "failure"));
|
||||
|
||||
if (translationUnit) {
|
||||
clang_visitChildren(clang_getTranslationUnitCursor(translationUnit->unit), ClangThread::visitor, this);
|
||||
if (mQueryMessage->flags() & QueryMessage::DumpCheckIncludes)
|
||||
checkIncludes();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mConnection->disconnected().disconnect(key);
|
||||
std::weak_ptr<Connection> conn = mConnection;
|
||||
EventLoop::mainEventLoop()->callLater([conn]() {
|
||||
if (auto c = conn.lock())
|
||||
c->finish();
|
||||
});
|
||||
}
|
||||
|
||||
void ClangThread::writeToConnetion(const String &message)
|
||||
{
|
||||
std::weak_ptr<Connection> conn = mConnection;
|
||||
EventLoop::mainEventLoop()->callLater([conn, message]() {
|
||||
if (auto c = conn.lock()) {
|
||||
c->write(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ClangThread::handleInclude(Location loc, const CXCursor &cursor)
|
||||
{
|
||||
CXFile includedFile = clang_getIncludedFile(cursor);
|
||||
if (includedFile) {
|
||||
CXStringScope fn = clang_getFileName(includedFile);
|
||||
const char *cstr = clang_getCString(fn);
|
||||
if (!cstr) {
|
||||
clang_disposeString(fn);
|
||||
return;
|
||||
}
|
||||
const Path p = Path::resolved(cstr);
|
||||
clang_disposeString(fn);
|
||||
const uint32_t fileId = Location::insertFile(p);
|
||||
Dep *&source = mDependencies[loc.fileId()];
|
||||
if (!source)
|
||||
source = new Dep(loc.fileId());
|
||||
Dep *&include = mDependencies[fileId];
|
||||
if (!include)
|
||||
include = new Dep(fileId);
|
||||
source->include(include);
|
||||
}
|
||||
}
|
||||
|
||||
void ClangThread::handleReference(Location loc, const CXCursor &ref)
|
||||
{
|
||||
if (clang_getCursorKind(ref) == CXCursor_Namespace)
|
||||
return;
|
||||
const Location refLoc = RTags::createLocation(ref);
|
||||
if (refLoc.isNull() || refLoc.fileId() == loc.fileId())
|
||||
return;
|
||||
|
||||
Dep *dep = mDependencies[loc.fileId()];
|
||||
assert(dep);
|
||||
Dep *refDep = mDependencies[refLoc.fileId()];
|
||||
assert(refDep);
|
||||
auto &refs = dep->references[refDep->fileId];
|
||||
refs[loc] = refLoc;
|
||||
}
|
||||
|
||||
void ClangThread::checkIncludes(Location location, const CXCursor &cursor)
|
||||
{
|
||||
if (clang_getCursorKind(cursor) == CXCursor_InclusionDirective) {
|
||||
handleInclude(location, cursor);
|
||||
} else {
|
||||
const CXCursor ref = clang_getCursorReferenced(cursor);
|
||||
if (RTags::isValid(cursor) && cursor != ref) {
|
||||
handleReference(location, ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool validateHasInclude(uint32_t ref, const Dep *cur, Set<uint32_t> &seen)
|
||||
{
|
||||
assert(ref);
|
||||
assert(cur);
|
||||
if (cur->includes.contains(ref))
|
||||
return true;
|
||||
if (!seen.insert(ref))
|
||||
return false;
|
||||
for (const auto &pair : cur->includes) {
|
||||
if (validateHasInclude(ref, static_cast<const Dep*>(pair.second), seen))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool validateNeedsInclude(const Dep *source, const Dep *header, Set<uint32_t> &seen)
|
||||
{
|
||||
if (!seen.insert(header->fileId)) {
|
||||
// error() << "already seen" << Location::path(source->fileId);
|
||||
return false;
|
||||
}
|
||||
if (source->references.contains(header->fileId)) {
|
||||
// error() << "Got ref" << Location::path(header->fileId);
|
||||
return true;
|
||||
}
|
||||
for (const auto &child : header->includes) {
|
||||
// error() << "Checking child" << Location::path(child.second->fileId);
|
||||
if (validateNeedsInclude(source, static_cast<const Dep*>(child.second), seen)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// error() << "Checking" << Location::path(source->fileId) << "doesn't seem to need" << Location::path(header->fileId) << depth;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ClangThread::checkIncludes()
|
||||
{
|
||||
for (const auto &it : mDependencies) {
|
||||
const Path path = Location::path(it.first);
|
||||
if (path.isSystem())
|
||||
continue;
|
||||
|
||||
for (const auto &dep : it.second->includes) {
|
||||
Set<uint32_t> seen;
|
||||
if (!validateNeedsInclude(it.second, static_cast<Dep*>(dep.second), seen)) {
|
||||
writeToConnetion(String::format<128>("%s includes %s for no reason",
|
||||
path.constData(),
|
||||
Location::path(dep.second->fileId).constData()));
|
||||
}
|
||||
}
|
||||
|
||||
continue; // the rest of this doeesn't really work that well.
|
||||
|
||||
for (const auto &ref : it.second->references) {
|
||||
const Path refPath = Location::path(ref.first);
|
||||
if (refPath.startsWith("/usr/include/sys/_types/_") || refPath.startsWith("/usr/include/_types/_"))
|
||||
continue;
|
||||
Set<uint32_t> seen;
|
||||
if (!validateHasInclude(ref.first, it.second, seen)) {
|
||||
List<String> reasons;
|
||||
for (const auto &r : ref.second) {
|
||||
String reason;
|
||||
Log log(&reason);
|
||||
log << r.first << "=>" << r.second;
|
||||
reasons << reason;
|
||||
}
|
||||
writeToConnetion(String::format<128>("%s should include %s (%s)",
|
||||
Location::path(it.first).constData(),
|
||||
Location::path(ref.first).constData(),
|
||||
String::join(reasons, " ").constData()));
|
||||
// for (const auto &incs : mDependencies[ref.first]->dependents) {
|
||||
// writeToConnetion(String::format<128>("GOT INCLUDER %s:%d", Location::path(incs.first).constData(),
|
||||
// incs.first));
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it : mDependencies) {
|
||||
delete it.second;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef ClangThread_h
|
||||
#define ClangThread_h
|
||||
|
||||
#include <clang-c/Index.h>
|
||||
|
||||
#include "Project.h"
|
||||
#include "QueryMessage.h"
|
||||
#include "rct/Thread.h"
|
||||
#include "rct/Value.h"
|
||||
#include "Source.h"
|
||||
|
||||
class Connection;
|
||||
struct Dep;
|
||||
class ClangThread : public Thread
|
||||
{
|
||||
public:
|
||||
ClangThread(const std::shared_ptr<QueryMessage> &queryMessage,
|
||||
const Source &source,
|
||||
const std::shared_ptr<Connection> &conn);
|
||||
~ClangThread() override;
|
||||
virtual void run() override;
|
||||
void abort() { std::unique_lock<std::mutex> lock(mMutex); mAborted = false; }
|
||||
bool isAborted() const { std::unique_lock<std::mutex> lock(mMutex); return mAborted; }
|
||||
private:
|
||||
static CXChildVisitResult visitor(CXCursor cursor, CXCursor, CXClientData userData);
|
||||
CXChildVisitResult visit(const CXCursor &cursor);
|
||||
void checkIncludes(Location location, const CXCursor &cursor);
|
||||
|
||||
void writeToConnetion(const String &message);
|
||||
void handleInclude(Location loc, const CXCursor &cursor);
|
||||
void handleReference(Location loc, const CXCursor &ref);
|
||||
void checkIncludes();
|
||||
|
||||
const std::shared_ptr<QueryMessage> mQueryMessage;
|
||||
const Source mSource;
|
||||
std::shared_ptr<Connection> mConnection;
|
||||
int mIndentLevel;
|
||||
mutable std::mutex mMutex;
|
||||
Hash<uint32_t, Dep*> mDependencies;
|
||||
Hash<Path, String> mContextCache;
|
||||
bool mAborted;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,80 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "ClassHierarchyJob.h"
|
||||
|
||||
#include "Project.h"
|
||||
#include "RTags.h"
|
||||
#include "Server.h"
|
||||
|
||||
ClassHierarchyJob::ClassHierarchyJob(Location loc,
|
||||
const std::shared_ptr<QueryMessage> &query,
|
||||
const std::shared_ptr<Project> &project)
|
||||
: QueryJob(query, project), location(loc)
|
||||
{
|
||||
}
|
||||
|
||||
int ClassHierarchyJob::execute()
|
||||
{
|
||||
Symbol symbol = project()->findSymbol(location);
|
||||
if (symbol.isNull())
|
||||
return 1;
|
||||
if (!symbol.isClass() || !symbol.isDefinition())
|
||||
symbol = project()->findTarget(symbol.location);
|
||||
if (!symbol.isClass())
|
||||
return 1;
|
||||
|
||||
typedef std::function<Set<Symbol>(const Symbol &)> FindFunc;
|
||||
std::function<void (const Symbol &, const char *title, int, FindFunc)> recurse = [&](const Symbol &sym,
|
||||
const char *title,
|
||||
int indent,
|
||||
FindFunc find)
|
||||
{
|
||||
auto classes = find(sym);
|
||||
if (!indent) {
|
||||
if (classes.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
write(title);
|
||||
indent = 2;
|
||||
write<256>(" %s\t%s",
|
||||
sym.symbolName.constData(),
|
||||
sym.location.toString(locationToStringFlags()).constData());
|
||||
} else {
|
||||
write<256>("%s%s\t%s",
|
||||
String(indent, ' ').constData(),
|
||||
sym.symbolName.constData(),
|
||||
sym.location.toString(locationToStringFlags()).constData());
|
||||
}
|
||||
for (const Symbol &c : classes) {
|
||||
recurse(c, title, indent + 2, find);
|
||||
}
|
||||
};
|
||||
|
||||
recurse(symbol, "Superclasses:", 0, [this](const Symbol &sym) {
|
||||
Set<Symbol> ret;
|
||||
for (const String &usr : sym.baseClasses) {
|
||||
for (const auto &s : project()->findByUsr(usr, sym.location.fileId(), Project::ArgDependsOn)) {
|
||||
if (s.isDefinition()) {
|
||||
ret.insert(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
recurse(symbol, "Subclasses:", 0, [this](const Symbol &sym) { return project()->findSubclasses(sym); });
|
||||
return 0;
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef ClassHierarchyJob_h
|
||||
#define ClassHierarchyJob_h
|
||||
|
||||
#include "Location.h"
|
||||
#include "QueryJob.h"
|
||||
|
||||
class ClassHierarchyJob : public QueryJob
|
||||
{
|
||||
public:
|
||||
ClassHierarchyJob(Location loc, const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Project> &project);
|
||||
protected:
|
||||
virtual int execute() override;
|
||||
private:
|
||||
const Location location;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,316 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef CommandLineParser_h
|
||||
#define CommandLineParser_h
|
||||
|
||||
namespace CommandLineParser {
|
||||
enum ValueType {
|
||||
Required,
|
||||
Optional,
|
||||
NoValue
|
||||
};
|
||||
template <typename T>
|
||||
struct Option {
|
||||
const T option;
|
||||
const String longOpt;
|
||||
const char shortOpt;
|
||||
ValueType valueType;
|
||||
const String description;
|
||||
};
|
||||
enum Status {
|
||||
Parse_Exec,
|
||||
Parse_Ok,
|
||||
Parse_Error
|
||||
};
|
||||
|
||||
struct ParseStatus {
|
||||
String error;
|
||||
Status status;
|
||||
};
|
||||
enum Flag {
|
||||
NoFlag = 0x0,
|
||||
IgnoreUnknown = 0x1
|
||||
};
|
||||
|
||||
enum ConfigOptionType {
|
||||
ConfigNone = 0,
|
||||
Config,
|
||||
NoRc
|
||||
};
|
||||
|
||||
RCT_FLAGS(Flag);
|
||||
template <typename T>
|
||||
ParseStatus parse(int argc, char **argv,
|
||||
std::initializer_list<Option<T> > optsList,
|
||||
Flags<Flag> flags,
|
||||
const std::function<ParseStatus(T, String &&value, size_t &idx, const List<String> &args)> &handler,
|
||||
const String &app = String(),
|
||||
std::initializer_list<Option<ConfigOptionType> > configOpts = std::initializer_list<Option<ConfigOptionType> >(),
|
||||
String *cmdLine = 0)
|
||||
{
|
||||
Hash<String, const Option<T> *> longOpts;
|
||||
Hash<char, const Option<T> *> shortOpts;
|
||||
for (const auto &opt : optsList) {
|
||||
if (!opt.longOpt.isEmpty())
|
||||
longOpts[opt.longOpt] = &opt;
|
||||
if (opt.shortOpt)
|
||||
shortOpts[opt.shortOpt] = &opt;
|
||||
}
|
||||
if (getenv("RTAGS_DUMP_UNUSED")) {
|
||||
String unused;
|
||||
for (int i=0; i<26; ++i) {
|
||||
if (!shortOpts.contains('a' + i))
|
||||
unused.append('a' + i);
|
||||
if (!shortOpts.contains('A' + i))
|
||||
unused.append('A' + i);
|
||||
}
|
||||
printf("Unused: %s\n", unused.constData());
|
||||
for (const auto &opt : optsList) {
|
||||
if (!opt.longOpt.isEmpty()) {
|
||||
if (!opt.shortOpt) {
|
||||
printf("No shortoption for %s\n", opt.longOpt.constData());
|
||||
} else if (opt.longOpt[0] != opt.shortOpt) {
|
||||
printf("Not ideal option for %s|%c\n", opt.longOpt.constData(), opt.shortOpt);
|
||||
}
|
||||
}
|
||||
}
|
||||
return { String(), Parse_Ok };
|
||||
}
|
||||
|
||||
List<String> args;
|
||||
if (configOpts.size() && !app.isEmpty()) {
|
||||
bool norc = false;
|
||||
Path rcfile = Path::home() + "." + app + "rc";
|
||||
if (!rcfile.exists()) {
|
||||
const char * configPath = getenv("XDG_CONFIG_DIR");
|
||||
rcfile = configPath ? configPath : Path::home() + ".config";
|
||||
rcfile += "/rtags/";
|
||||
rcfile.mkdir(Path::Recursive);
|
||||
rcfile += app + "rc";
|
||||
}
|
||||
parse<ConfigOptionType>(argc, argv, configOpts,
|
||||
IgnoreUnknown, [&norc, &rcfile](ConfigOptionType type, String &&value, size_t &, const List<String> &) -> ParseStatus {
|
||||
switch (type) {
|
||||
case ConfigNone: {
|
||||
assert(0);
|
||||
break; }
|
||||
case Config: {
|
||||
rcfile = std::move(value);
|
||||
break; }
|
||||
case NoRc: {
|
||||
norc = true;
|
||||
break; }
|
||||
}
|
||||
return { String(), Parse_Exec };
|
||||
});
|
||||
if (!norc) {
|
||||
args.push_back(argv[0]);
|
||||
String rc = Path("/etc/rcrc").readAll();
|
||||
if (!rc.isEmpty()) {
|
||||
for (const String &s : rc.split('\n')) {
|
||||
if (!s.isEmpty() && !s.startsWith('#'))
|
||||
args += s.split(' ');
|
||||
}
|
||||
}
|
||||
if (!rcfile.isEmpty()) {
|
||||
rc = rcfile.readAll();
|
||||
if (!rc.isEmpty()) {
|
||||
for (const String& s : rc.split('\n')) {
|
||||
if (!s.isEmpty() && !s.startsWith('#'))
|
||||
args += s.split(' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i=1; i<argc; ++i)
|
||||
args.append(argv[i]);
|
||||
}
|
||||
}
|
||||
if (args.isEmpty()) {
|
||||
args.resize(argc);
|
||||
for (int i=0; i<argc; ++i) {
|
||||
args[i] = argv[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (cmdLine) {
|
||||
bool first = true;
|
||||
for (const String &arg : args) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
cmdLine->append(' ');
|
||||
}
|
||||
const bool space = arg.contains(' ');
|
||||
if (space)
|
||||
cmdLine->append('"');
|
||||
cmdLine->append(arg);
|
||||
if (space)
|
||||
cmdLine->append('"');
|
||||
}
|
||||
}
|
||||
|
||||
ParseStatus status = { String(), Parse_Exec };
|
||||
for (size_t i=1; i<args.size(); ++i) {
|
||||
const String &arg = args.at(i);
|
||||
List<const Option<T> *> opts;
|
||||
String value;
|
||||
auto addArg = [&arg, &opts, &status, flags](const Option<T> *opt) -> bool {
|
||||
if (opt) {
|
||||
opts.append(opt);
|
||||
return true;
|
||||
}
|
||||
if (flags & IgnoreUnknown)
|
||||
return true;
|
||||
|
||||
status = { String::format<1024>("Couldn't parse argument %s", arg.constData()), Parse_Error };
|
||||
return false;
|
||||
};
|
||||
if (arg == "--") {
|
||||
addArg(longOpts.value("--"));
|
||||
} else if (arg.startsWith("--")) {
|
||||
const size_t eq = arg.indexOf('=');
|
||||
String a;
|
||||
if (eq == String::npos) {
|
||||
a = arg.mid(2);
|
||||
} else {
|
||||
a = arg.mid(2, eq - 2);
|
||||
value = arg.mid(eq + 1);
|
||||
}
|
||||
addArg(longOpts.value(a));
|
||||
} else if (arg.startsWith("-")) {
|
||||
for (size_t j=1; j<arg.size(); ++j) {
|
||||
if (j > 1 && !opts.isEmpty() && opts.back()->valueType != NoValue) {
|
||||
if (arg.at(j) == '=')
|
||||
++j;
|
||||
value = arg.mid(j);
|
||||
break;
|
||||
}
|
||||
|
||||
addArg(shortOpts.value(arg.at(j)));
|
||||
}
|
||||
} else {
|
||||
addArg(0);
|
||||
}
|
||||
|
||||
for (const Option<T> *opt : opts) {
|
||||
switch (opt->valueType) {
|
||||
case Required:
|
||||
if (value.isEmpty() && i + 1 < args.size())
|
||||
value = args.at(++i);
|
||||
status = handler(opt->option, std::move(value), i, args);
|
||||
break;
|
||||
case Optional:
|
||||
if (value.isEmpty() && i + 1 < args.size() && (!args.at(i + 1).startsWith('-') || args.at(i + 1).size() == 1))
|
||||
value = args.at(++i);
|
||||
status = handler(opt->option, std::move(value), i, args);
|
||||
break;
|
||||
case NoValue:
|
||||
status = handler(opt->option, String(), i, args);
|
||||
break;
|
||||
}
|
||||
if (status.status != Parse_Exec)
|
||||
break;
|
||||
}
|
||||
if (status.status != Parse_Exec)
|
||||
break;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void help(FILE *f, const char *app, std::initializer_list<Option<T> > optsList)
|
||||
{
|
||||
List<String> out;
|
||||
size_t longest = 0;
|
||||
for (const auto &opt : optsList) {
|
||||
if (opt.longOpt.isEmpty() && !opt.shortOpt) {
|
||||
out.append(String());
|
||||
} else {
|
||||
out.append(String::format<64>(" %s%s%s%s",
|
||||
(opt.longOpt.isEmpty() ? String() : ("--" + opt.longOpt)).constData(),
|
||||
!opt.longOpt.isEmpty() && opt.shortOpt ? "|" : "",
|
||||
opt.shortOpt ? String::format<2>("-%c", opt.shortOpt).constData() : "",
|
||||
opt.valueType == Required ? " [arg] "
|
||||
: opt.valueType == Optional ? " [optional] " : ""));
|
||||
longest = std::max<size_t>(out.back().size(), longest);
|
||||
}
|
||||
}
|
||||
fprintf(f, "%s options...\n", app);
|
||||
const Option<T> *opts = optsList.begin();
|
||||
const size_t c = out.size();
|
||||
for (size_t i=0; i<c; ++i) {
|
||||
if (out.at(i).isEmpty()) {
|
||||
fprintf(f, "%s\n", opts[i].description.constData());
|
||||
} else {
|
||||
fprintf(f, "%s%s %s\n",
|
||||
out.at(i).constData(),
|
||||
String(longest - out.at(i).size(), ' ').constData(),
|
||||
opts[i].description.constData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void man(std::initializer_list<Option<T> > optsList)
|
||||
{
|
||||
const Option<T> *opts = optsList.begin();
|
||||
String out = ("<!DOCTYPE manpage SYSTEM \"http://masqmail.cx/xmltoman/xmltoman.dtd\">\n"
|
||||
"<?xml-stylesheet type=\"text/xsl\" href=\"http://masqmail.cx/xmltoman/xmltoman.xsl\"?>\n"
|
||||
"\n"
|
||||
"<manpage name=\"rc\" section=\"1\" desc=\"command line client for RTags\">\n"
|
||||
"\n"
|
||||
"<synopsis>\n"
|
||||
" <cmd>rc <arg>file.1.xml</arg> > file.1</cmd>\n"
|
||||
"</synopsis>\n"
|
||||
"\n"
|
||||
"<description>\n"
|
||||
"\n"
|
||||
"<p>rc is a command line client used to control RTags.</p>\n"
|
||||
"\n"
|
||||
"</description>\n");
|
||||
for (size_t i=0; i<optsList.size(); ++i) {
|
||||
if (!opts[i].description.isEmpty()) {
|
||||
if (opts[i].longOpt.isEmpty() && !opts[i].shortOpt) {
|
||||
if (i)
|
||||
out.append("</section>\n");
|
||||
out.append(String::format<128>("<section name=\"%s\">\n", opts[i].description.constData()));
|
||||
} else {
|
||||
out.append(String::format<64>(" <option>%s%s%s%s<optdesc>%s</optdesc></option>\n",
|
||||
(opts[i].longOpt.isEmpty() ? String() : ("--" + opts[i].longOpt)).constData(),
|
||||
!opts[i].longOpt.isEmpty() && opts[i].shortOpt ? "|" : "",
|
||||
opts[i].shortOpt ? String::format<2>("-%c", opts[i].shortOpt).constData() : "",
|
||||
opts[i].valueType == Required ? " [arg] " : opts[i].valueType == Optional ? " [optional] " : "",
|
||||
opts[i].description.constData()));
|
||||
}
|
||||
}
|
||||
}
|
||||
out.append("</section>\n"
|
||||
"<section name=\"Authors\">\n"
|
||||
" <p>RTags was written by Jan Erik Hanssen <jhanssen@gmail.com> and Anders Bakken <abakken@gmail.com></p>\n"
|
||||
"</section>\n"
|
||||
"<section name=\"See also\">\n"
|
||||
" <p><manref name=\"rdm\" section=\"1\"/></p>\n"
|
||||
"</section>\n"
|
||||
"<section name=\"Comments\">\n"
|
||||
" <p>This man page was written using <manref name=\"xmltoman\" section=\"1\" href=\"http://masqmail.cx/xmltoman/\"/>.</p>\n"
|
||||
"</section>\n"
|
||||
"</manpage>\n");
|
||||
printf("%s", out.constData());
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,196 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "CompilerManager.h"
|
||||
|
||||
#include "rct/Log.h"
|
||||
#include "rct/Process.h"
|
||||
#include "Source.h"
|
||||
|
||||
static std::mutex sMutex;
|
||||
struct Compiler {
|
||||
Compiler()
|
||||
: inited(false)
|
||||
{}
|
||||
bool inited;
|
||||
|
||||
// There are three include-path-limiting options:
|
||||
// 1. -nostdinc -- disables all default system include paths
|
||||
// Example: <string.h> (stuff under /usr/include, etc.)
|
||||
// 2. -nostdinc++ -- disables C++ standard include path
|
||||
// Example: <vector> (stuff under /usr/lib/c++/v1)
|
||||
// 3. -nobuiltininc -- (clang only?) disables compiler-provided includes
|
||||
// Example: limits.h, float.h
|
||||
|
||||
Set<Source::Define> defines;
|
||||
List<Source::Include> includePaths;
|
||||
List<Source::Include> stdincxxPaths;
|
||||
List<Source::Include> builtinPaths;
|
||||
};
|
||||
static Hash<Path, Compiler> sCompilers;
|
||||
|
||||
namespace CompilerManager {
|
||||
|
||||
List<Path> compilers()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(sMutex);
|
||||
return sCompilers.keys();
|
||||
}
|
||||
|
||||
void applyToSource(Source &source, Flags<CompilerManager::Flag> flags)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(sMutex);
|
||||
Path cpath = source.compiler();
|
||||
Compiler &compiler = sCompilers[cpath];
|
||||
if (!compiler.inited) {
|
||||
compiler.inited = true;
|
||||
|
||||
List<String> overrides;
|
||||
List<String> out, err;
|
||||
List<String> args;
|
||||
List<String> environ({"RTAGS_DISABLED=1"});
|
||||
args << "-x" << "c++" << "-v" << "-E" << "-dM" << "-";
|
||||
|
||||
for (int i=0; i<4; /* see below */) {
|
||||
Process proc;
|
||||
proc.exec(cpath, args, environ);
|
||||
assert(proc.isFinished());
|
||||
if (!proc.returnCode()) {
|
||||
out << proc.readAllStdOut().split('\n');
|
||||
err << proc.readAllStdErr().split('\n');
|
||||
|
||||
// proc success. What's next?
|
||||
switch (i) {
|
||||
case 0:
|
||||
// C++ ok .. see which path is controlled by -nostdinc++
|
||||
args.prepend("-nostdinc++");
|
||||
err << "@@@@\n"; // magic separator
|
||||
i = 2;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// "-x c++" not ok. Goto -nobuiltininc.
|
||||
err << "@@@@\n"; // magic separator
|
||||
args.prepend("-nobuiltininc");
|
||||
i = 3;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
args.removeFirst(); // clear -nostdinc++
|
||||
err << "@@@@\n"; // magic separator
|
||||
args.prepend("-nobuiltininc");
|
||||
i = 3;
|
||||
break;
|
||||
|
||||
default:
|
||||
err << "@@@@\n"; // magic separator
|
||||
i = 4;
|
||||
break;
|
||||
}
|
||||
} else if (i == 0) {
|
||||
// Strip -x c++ and try again
|
||||
args.removeFirst();
|
||||
args.removeFirst();
|
||||
i = 1;
|
||||
} else if (i == 3) {
|
||||
// GCC does not support -nobuiltininc flag.
|
||||
// Remove and retry
|
||||
args.removeFirst();
|
||||
} else {
|
||||
error() << "CompilerManager: Cannot extract standard include paths.\n";
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (size_t i=0; i<out.size(); ++i) {
|
||||
const String &line = out.at(i);
|
||||
// error() << c << line;
|
||||
if (line.startsWith("#define ")) {
|
||||
Source::Define def;
|
||||
const int space = line.indexOf(' ', 8);
|
||||
if (space == -1) {
|
||||
def.define = line.mid(8);
|
||||
} else {
|
||||
def.define = line.mid(8, space - 8);
|
||||
def.value = line.mid(space + 1);
|
||||
}
|
||||
compiler.defines.insert(def);
|
||||
}
|
||||
}
|
||||
|
||||
enum { eNormal, eNoStdInc, eNoBuiltin } mode = eNormal;
|
||||
List<Source::Include> copy;
|
||||
for (size_t i=0; i<err.size(); ++i) {
|
||||
const String &line = err.at(i);
|
||||
if (line.startsWith("@@@@")) { // magic separator
|
||||
if (mode == eNoStdInc) {
|
||||
// What's left in copy are the std c++ paths
|
||||
compiler.stdincxxPaths = copy;
|
||||
mode = eNoBuiltin;
|
||||
} else if (mode == eNoBuiltin) {
|
||||
// What's left in copy are the builtin paths
|
||||
compiler.builtinPaths = copy;
|
||||
// Set the includePaths exclusive of stdinc/builtin
|
||||
for (auto& inc : compiler.stdincxxPaths)
|
||||
compiler.includePaths.remove(inc);
|
||||
for (auto& inc : compiler.builtinPaths)
|
||||
compiler.includePaths.remove(inc);
|
||||
break; // we're done
|
||||
} else {
|
||||
mode = eNoStdInc;
|
||||
}
|
||||
copy = compiler.includePaths;
|
||||
}
|
||||
size_t j = 0;
|
||||
while (j < line.size() && isspace(line.at(j)))
|
||||
++j;
|
||||
int end = line.lastIndexOf(" (framework directory)");
|
||||
Source::Include::Type type = Source::Include::Type::Type_System;
|
||||
if (end != -1) {
|
||||
end = end - j;
|
||||
type = Source::Include::Type_SystemFramework;
|
||||
}
|
||||
Path path = line.mid(j, end);
|
||||
// error() << "looking at" << line << path << path.isDir();
|
||||
if (path.isDir()) {
|
||||
path.resolve();
|
||||
if (mode == eNormal) {
|
||||
compiler.includePaths.append(Source::Include(type, path));
|
||||
} else {
|
||||
copy.remove(Source::Include(type, path));
|
||||
}
|
||||
}
|
||||
}
|
||||
debug() << "[CompilerManager]" << cpath << "got includepaths\n" << compiler.includePaths;
|
||||
debug() << "StdInc++: " << compiler.stdincxxPaths << "\nBuiltin: " << compiler.builtinPaths;
|
||||
debug() << "[CompilerManager] returning.\n";
|
||||
}
|
||||
if (flags & IncludeDefines)
|
||||
source.defines << compiler.defines;
|
||||
if (flags & IncludeIncludePaths) {
|
||||
if (!source.arguments.contains("-nostdinc")) {
|
||||
source.includePaths << compiler.includePaths;
|
||||
if (!source.arguments.contains("-nostdinc++"))
|
||||
source.includePaths << compiler.stdincxxPaths;
|
||||
if (!source.arguments.contains("-nobuiltininc"))
|
||||
source.includePaths << compiler.builtinPaths;
|
||||
} else if (!strncmp("clang", cpath.fileName(), 5)) {
|
||||
// Module.map causes errors when -nostdinc is used, as it
|
||||
// can't find some mappings to compiler provided headers
|
||||
source.arguments.append("-fno-modules");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace CompilerManager
|
||||
@@ -1,38 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef CompilerManager_h
|
||||
#define CompilerManager_h
|
||||
|
||||
#include "rct/List.h"
|
||||
#include "rct/Path.h"
|
||||
#include "rct/Serializer.h"
|
||||
#include "rct/Flags.h"
|
||||
|
||||
struct Source;
|
||||
|
||||
namespace CompilerManager
|
||||
{
|
||||
List<Path> compilers();
|
||||
enum Flag {
|
||||
None = 0x0,
|
||||
IncludeDefines = 0x1,
|
||||
IncludeIncludePaths = 0x2
|
||||
};
|
||||
RCT_FLAGS(Flag);
|
||||
void applyToSource(Source &source, Flags<Flag> flags);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,748 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "CompletionThread.h"
|
||||
#include "IndexDataMessage.h"
|
||||
#include "Project.h"
|
||||
|
||||
#include "rct/StopWatch.h"
|
||||
#include "RTags.h"
|
||||
#include "RTagsLogOutput.h"
|
||||
#include "Server.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include "rct/StringTokenizer.h"
|
||||
#ifdef HAS_JSON_H
|
||||
#include "rct/json/json.hpp"
|
||||
using namespace nlohmann;
|
||||
#endif
|
||||
|
||||
static uint64_t start = 0;
|
||||
#define LOG() \
|
||||
if (Server::instance()->options().options & Server::CompletionLogs) \
|
||||
error() << "CODE COMPLETION" << String::format<16>("%gs", static_cast<double>(Rct::monoMs() - ::start) / 1000.0)
|
||||
|
||||
|
||||
CompletionThread::CompletionThread(int cacheSize)
|
||||
: mShutdown(false), mCacheSize(cacheSize), mDump(0)
|
||||
{
|
||||
}
|
||||
|
||||
CompletionThread::~CompletionThread()
|
||||
{
|
||||
mCacheList.deleteAll();
|
||||
}
|
||||
|
||||
void CompletionThread::run()
|
||||
{
|
||||
while (true) {
|
||||
Request *request = 0;
|
||||
Dump *dump = 0;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
while (!mShutdown && mPending.isEmpty() && !mDump) {
|
||||
mCondition.wait(lock);
|
||||
}
|
||||
if (mShutdown) {
|
||||
for (auto it = mPending.begin(); it != mPending.end(); ++it) {
|
||||
delete *it;
|
||||
}
|
||||
mPending.clear();
|
||||
if (mDump) {
|
||||
std::unique_lock<std::mutex> dumpLock(mDump->mutex);
|
||||
mDump->done = true;
|
||||
mDump->cond.notify_one();
|
||||
mDump = 0;
|
||||
}
|
||||
break;
|
||||
} else if (mDump) {
|
||||
std::swap(dump, mDump);
|
||||
} else {
|
||||
assert(!mPending.isEmpty());
|
||||
request = mPending.takeFirst();
|
||||
}
|
||||
}
|
||||
if (dump) {
|
||||
std::unique_lock<std::mutex> lock(dump->mutex);
|
||||
Log out(&dump->string);
|
||||
for (SourceFile *cache = mCacheList.first(); cache; cache = cache->next) {
|
||||
out << cache->source
|
||||
<< "\nparseTime:" << cache->parseTime
|
||||
<< "\nreparseTime:" << cache->reparseTime
|
||||
<< "\ncompletions:" << cache->completions
|
||||
<< "\ncompletionTime:" << cache->codeCompleteTime
|
||||
<< (cache->completions
|
||||
? String::format<32>("(avg: %.2f)",
|
||||
(static_cast<double>(cache->codeCompleteTime) / cache->completions))
|
||||
: String())
|
||||
<< "\ntranslationUnit:" << cache->translationUnit << "\n";
|
||||
}
|
||||
dump->done = true;
|
||||
dump->cond.notify_one();
|
||||
} else {
|
||||
assert(request);
|
||||
process(request);
|
||||
delete request;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CompletionThread::completeAt(Source &&source, Location location,
|
||||
Flags<Flag> flags, String &&unsaved,
|
||||
const String &prefix,
|
||||
const std::shared_ptr<Connection> &conn)
|
||||
{
|
||||
if (Server::instance()->options().options & Server::CompletionLogs)
|
||||
error() << "CODE COMPLETION completeAt" << location << flags;
|
||||
Request *request = new Request({ std::forward<Source>(source), location, flags, std::forward<String>(unsaved), prefix, conn});
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
auto it = mPending.begin();
|
||||
while (it != mPending.end()) {
|
||||
if ((*it)->source == source) {
|
||||
delete *it;
|
||||
mPending.erase(it);
|
||||
break;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
mPending.push_front(request);
|
||||
mCondition.notify_one();
|
||||
}
|
||||
|
||||
void CompletionThread::prepare(Source &&source, String &&unsaved)
|
||||
{
|
||||
if (Server::instance()->options().options & Server::CompletionLogs)
|
||||
error() << "CODE COMPLETION prepare" << source.sourceFile() << unsaved.size();
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
for (auto req : mPending) {
|
||||
if (req->source == source) {
|
||||
req->unsaved = std::move(unsaved);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Request *request = new Request({ std::forward<Source>(source), Location(), WarmUp, std::forward<String>(unsaved), String(), std::shared_ptr<Connection>() });
|
||||
mPending.push_back(request);
|
||||
mCondition.notify_one();
|
||||
}
|
||||
|
||||
String CompletionThread::dump()
|
||||
{
|
||||
Dump dump;
|
||||
dump.done = false;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
if (mDump)
|
||||
return String(); // dump in progress
|
||||
|
||||
mDump = &dump;
|
||||
mCondition.notify_one();
|
||||
}
|
||||
std::unique_lock<std::mutex> lock(dump.mutex);
|
||||
while (!dump.done) {
|
||||
dump.cond.wait(lock);
|
||||
}
|
||||
return std::move(dump.string);
|
||||
}
|
||||
|
||||
void CompletionThread::stop()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
mShutdown = true;
|
||||
mCondition.notify_one();
|
||||
}
|
||||
|
||||
bool CompletionThread::compareCompletionCandidates(const Completions::Candidate *l,
|
||||
const Completions::Candidate *r)
|
||||
{
|
||||
if (l->priority != r->priority)
|
||||
return l->priority < r->priority;
|
||||
#ifdef RTAGS_COMPLETION_TOKENS_ENABLED
|
||||
if ((l->distance != -1) != (r->distance != -1))
|
||||
return l->distance != -1;
|
||||
if (l->distance != r->distance)
|
||||
return l->distance > r->distance;
|
||||
#endif
|
||||
return l->completion < r->completion;
|
||||
}
|
||||
|
||||
void CompletionThread::process(Request *request)
|
||||
{
|
||||
::start = Rct::monoMs();
|
||||
LOG() << "processing" << request->toString();
|
||||
StopWatch sw;
|
||||
int parseTime = 0;
|
||||
int reparseTime = 0;
|
||||
int completeTime = 0;
|
||||
int processTime = 0;
|
||||
mMutex.lock();
|
||||
SourceFile *&cache = mCacheMap[request->source.fileId];
|
||||
|
||||
if (cache && cache->source != request->source) {
|
||||
LOG() << "cached sourcefile doesn't match source, discarding" << request->source.sourceFile();
|
||||
delete cache;
|
||||
cache = 0;
|
||||
}
|
||||
if (!cache) {
|
||||
cache = new SourceFile;
|
||||
LOG() << "creating source file for" << request->source.sourceFile();
|
||||
mCacheList.append(cache);
|
||||
while (mCacheMap.size() > mCacheSize) {
|
||||
SourceFile *c = mCacheList.removeFirst();
|
||||
LOG() << "over cache limit. discarding" << c->source.sourceFile();
|
||||
mCacheMap.remove(c->source.fileId);
|
||||
delete c;
|
||||
}
|
||||
} else {
|
||||
mCacheList.moveToEnd(cache);
|
||||
}
|
||||
mMutex.unlock();
|
||||
|
||||
assert(!cache->translationUnit || cache->source == request->source);
|
||||
if (!cache->translationUnit) {
|
||||
cache->source = request->source;
|
||||
}
|
||||
|
||||
const Path sourceFile = request->source.sourceFile();
|
||||
CXUnsavedFile unsaved = {
|
||||
sourceFile.constData(),
|
||||
request->unsaved.constData(),
|
||||
static_cast<unsigned long>(request->unsaved.size())
|
||||
};
|
||||
|
||||
const auto &options = Server::instance()->options();
|
||||
bool reparse = false;
|
||||
if (!cache->translationUnit) {
|
||||
if (request->conn && request->flags & NoWait) {
|
||||
request->flags |= WarmUp;
|
||||
if (request->flags & Elisp) {
|
||||
request->conn->finish("(list (cons 'pending t))");
|
||||
} else if (request->flags & XML) {
|
||||
request->conn->finish("<?xml version=\"1.0\" encoding=\"utf-8\"?><completions pending=\"true\">");
|
||||
} else if (request->flags & JSON) {
|
||||
request->conn->finish("{\"pending\":true}");
|
||||
} else {
|
||||
request->conn->finish("pending");
|
||||
}
|
||||
request->conn.reset();
|
||||
}
|
||||
LOG() << "No translationUnit for" << request->source.sourceFile() << "recreating";
|
||||
sw.restart();
|
||||
Flags<CXTranslationUnit_Flags> flags = static_cast<CXTranslationUnit_Flags>(clang_defaultEditingTranslationUnitOptions());
|
||||
flags |= CXTranslationUnit_CacheCompletionResults;
|
||||
flags |= CXTranslationUnit_DetailedPreprocessingRecord;
|
||||
flags |= CXTranslationUnit_Incomplete;
|
||||
flags |= CXTranslationUnit_IncludeBriefCommentsInCodeCompletion;
|
||||
flags |= CXTranslationUnit_PrecompiledPreamble;
|
||||
#if CINDEX_VERSION >= CINDEX_VERSION_ENCODE(0, 32)
|
||||
flags |= CXTranslationUnit_CreatePreambleOnFirstParse;
|
||||
#endif
|
||||
for (const auto &inc : options.includePaths) {
|
||||
request->source.includePaths << inc;
|
||||
}
|
||||
request->source.defines << options.defines;
|
||||
|
||||
cache->translationUnit = RTags::TranslationUnit::create(sourceFile,
|
||||
request->source.toCommandLine(Source::Default|Source::ExcludeDefaultArguments),
|
||||
&unsaved, request->unsaved.size() ? 1 : 0, flags, true);
|
||||
// error() << "PARSING" << clangLine;
|
||||
parseTime = cache->parseTime = sw.elapsed();
|
||||
// with clang 3.8 it definitely seems like we have to reparse once to
|
||||
// generate the preamble. Even with CXTranslationUnit_CreatePreambleOnFirstParse
|
||||
if (!cache->translationUnit) {
|
||||
LOG() << "Failed to parse translation unit" << request->source.sourceFile();
|
||||
return;
|
||||
}
|
||||
reparse = true;
|
||||
} else if (!request->unsaved.isEmpty()) {
|
||||
reparse = request->unsaved != cache->unsaved;
|
||||
cache->lastModified = 0;
|
||||
} else {
|
||||
const uint64_t lastModified = request->source.sourceFile().lastModifiedMs();
|
||||
if (lastModified != cache->lastModified) {
|
||||
cache->lastModified = lastModified;
|
||||
cache->unsaved.clear();
|
||||
reparse = true;
|
||||
} else {
|
||||
assert(cache->unsaved.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
LOG() << "line: " << request->location.line() << "column: "
|
||||
<< request->location.column() << ", prefix: "
|
||||
<< request->prefix;
|
||||
|
||||
if (reparse) {
|
||||
sw.restart();
|
||||
assert(cache->translationUnit);
|
||||
LOG() << "reparsing translation unit" << request->source.sourceFile();
|
||||
cache->translationUnit->reparse(&unsaved, request->unsaved.size() ? 1 : 0);
|
||||
reparseTime = cache->reparseTime = sw.elapsed();
|
||||
cache->unsaved = std::move(request->unsaved);
|
||||
}
|
||||
|
||||
|
||||
if (request->flags & WarmUp) {
|
||||
LOG() << "Warmed up unit" << request->source.sourceFile();
|
||||
return;
|
||||
}
|
||||
|
||||
sw.restart();
|
||||
unsigned int completionFlags = (CXCodeComplete_IncludeCodePatterns|CXCodeComplete_IncludeBriefComments);
|
||||
if (request->flags & IncludeMacros)
|
||||
completionFlags |= CXCodeComplete_IncludeMacros;
|
||||
|
||||
CXCodeCompleteResults *results = clang_codeCompleteAt(cache->translationUnit->unit, sourceFile.constData(),
|
||||
request->location.line(), request->location.column(),
|
||||
&unsaved, unsaved.Length ? 1 : 0, completionFlags);
|
||||
completeTime = cache->codeCompleteTime = sw.restart();
|
||||
LOG() << "Generated" << (results ? results->NumResults : 0) << "completions for" << request->location << (results ? "successfully" : "unsuccessfully") << "in" << completeTime << "ms";
|
||||
|
||||
++cache->completions;
|
||||
if (results) {
|
||||
List<CompletionCandidate *> candidates;
|
||||
candidates.reserve(results->NumResults);
|
||||
|
||||
int nodeCount = 0;
|
||||
#ifdef RTAGS_COMPLETION_TOKENS_ENABLED
|
||||
Map<Token, int> tokens;
|
||||
if (!request->unsaved.isEmpty()) {
|
||||
tokens = Token::tokenize(request->unsaved.constData(), request->unsaved.size());
|
||||
// for (Map<Token, int>::const_iterator it = tokens.begin(); it != tokens.end(); ++it) {
|
||||
// error() << String(it->first.data, it->first.length) << it->second;
|
||||
// }
|
||||
}
|
||||
#endif
|
||||
for (unsigned int i = 0; i < results->NumResults; ++i) {
|
||||
const CXCursorKind kind = results->Results[i].CursorKind;
|
||||
const CXCompletionString &string = results->Results[i].CompletionString;
|
||||
|
||||
const CXAvailabilityKind availabilityKind = clang_getCompletionAvailability(string);
|
||||
if (!(options.options & Server::CompletionsNoFilter)) {
|
||||
switch (availabilityKind) {
|
||||
case CXAvailability_Available:
|
||||
break;
|
||||
case CXAvailability_Deprecated:
|
||||
break;
|
||||
case CXAvailability_NotAccessible:
|
||||
break;
|
||||
case CXAvailability_NotAvailable: // protected members are erroneously flagged as NotAvailable in clang 3.6
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const int priority = clang_getCompletionPriority(string);
|
||||
|
||||
CompletionCandidate *candidate = new CompletionCandidate;
|
||||
candidate->kind = RTags::eatString(clang_getCursorKindSpelling(kind));
|
||||
candidate->priority = priority;
|
||||
candidate->parent = RTags::eatString(clang_getCompletionParent(string, 0));
|
||||
candidate->brief_comment = RTags::eatString(clang_getCompletionBriefComment(string));
|
||||
|
||||
candidates.push_back(candidate);
|
||||
|
||||
bool ok = true;
|
||||
const int chunkCount = clang_getNumCompletionChunks(string);
|
||||
for (int j=0; j<chunkCount; ++j) {
|
||||
const CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(string, j);
|
||||
String text = RTags::eatString(clang_getCompletionChunkText(string, j));
|
||||
if (chunkKind == CXCompletionChunk_TypedText) {
|
||||
candidate->name = text;
|
||||
if (candidate->name.isEmpty()) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
candidate->signature += candidate->name;
|
||||
} else {
|
||||
candidate->signature += text;
|
||||
if (chunkKind == CXCompletionChunk_ResultType)
|
||||
candidate->signature += ' ';
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
const unsigned int annotations = clang_getCompletionNumAnnotations(string);
|
||||
for (unsigned j=0; j<annotations; ++j) {
|
||||
const CXStringScope annotation = clang_getCompletionAnnotation(string, j);
|
||||
const char *cstr = clang_getCString(annotation);
|
||||
if (strlen(cstr)) {
|
||||
if (!candidate->annotation.isEmpty())
|
||||
candidate->annotation += ' ';
|
||||
candidate->annotation + cstr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<std::unique_ptr<MatchResult> > matches = StringTokenizer::find_and_sort_matches(candidates, request->prefix);
|
||||
|
||||
if (!matches.isEmpty()) {
|
||||
printCompletions(matches, request);
|
||||
processTime = sw.elapsed();
|
||||
LOG() << "Sent" << matches.size() << "completions for" << request->location;
|
||||
warning("Processed %s, parse %d/%d, complete %d, process %d => %d completions (unsaved %zu)",
|
||||
request->location.toString().constData(),
|
||||
parseTime, reparseTime, completeTime, processTime, nodeCount, request->unsaved.size());
|
||||
|
||||
} else {
|
||||
LOG() << "No completions available for" << request->location;
|
||||
printCompletions(List<std::unique_ptr<MatchResult> >(), request);
|
||||
error() << "No completion results available" << request->location;
|
||||
}
|
||||
|
||||
processDiagnostics(request, results, cache->translationUnit->unit);
|
||||
clang_disposeCodeCompleteResults(results);
|
||||
}
|
||||
}
|
||||
|
||||
Value CompletionThread::Completions::Candidate::toValue(unsigned int f) const
|
||||
{
|
||||
Value ret;
|
||||
if (!completion.isEmpty())
|
||||
ret["completion"] = completion;
|
||||
if (!signature.isEmpty())
|
||||
ret["signature"] = signature;
|
||||
if (!annotation.isEmpty())
|
||||
ret["annotation"] = annotation;
|
||||
if (!parent.isEmpty())
|
||||
ret["parent"] = parent;
|
||||
if (!briefComment.isEmpty())
|
||||
ret["briefComment"] = briefComment;
|
||||
ret["priority"] = priority;
|
||||
#ifdef RTAGS_COMPLETION_TOKENS_ENABLED
|
||||
ret["distance"] = distance;
|
||||
#endif
|
||||
String str;
|
||||
str << cursorKind;
|
||||
ret["kind"] = str;
|
||||
if (f & IncludeChunks && !chunks.isEmpty()) {
|
||||
Value cc;
|
||||
cc.arrayReserve(chunks.size());
|
||||
for (const auto &chunk : chunks) {
|
||||
Value c;
|
||||
c["text"] = chunk.text;
|
||||
String kind;
|
||||
kind << chunk.kind;
|
||||
c["kind"] = kind;
|
||||
cc.push_back(c);
|
||||
}
|
||||
ret["chunks"] = cc;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct Output
|
||||
{
|
||||
void send(const String &string)
|
||||
{
|
||||
if (connection) {
|
||||
connection->write(string);
|
||||
connection->finish();
|
||||
} else {
|
||||
output->log(string.constData());
|
||||
}
|
||||
}
|
||||
std::shared_ptr<LogOutput> output;
|
||||
std::shared_ptr<Connection> connection;
|
||||
Flags<CompletionThread::Flag> flags;
|
||||
};
|
||||
|
||||
void CompletionThread::printCompletions(const List<std::unique_ptr<MatchResult> > &results, Request *request)
|
||||
{
|
||||
static List<String> cursorKindNames;
|
||||
// error() << request->flags << testLog(RTags::DiagnosticsLevel) << completions.size() << request->conn;
|
||||
List<std::shared_ptr<Output> > outputs;
|
||||
bool xml = false;
|
||||
bool elisp = false;
|
||||
bool raw = false;
|
||||
#ifdef HAS_JSON_H
|
||||
bool send_json = false;
|
||||
json j = {{"completions", json::array()}};
|
||||
#endif
|
||||
if (request->conn) {
|
||||
auto output = std::make_shared<Output>();
|
||||
output->connection = request->conn;
|
||||
output->flags = request->flags;
|
||||
outputs.append(output);
|
||||
if (request->flags & Elisp) {
|
||||
elisp = true;
|
||||
} else if (request->flags & XML) {
|
||||
xml = true;
|
||||
#ifdef HAS_JSON_H
|
||||
} else if (request->flags & JSON) {
|
||||
send_json = true;
|
||||
#endif
|
||||
} else {
|
||||
raw = true;
|
||||
}
|
||||
request->conn.reset();
|
||||
} else {
|
||||
log([&](const std::shared_ptr<LogOutput> &output) {
|
||||
// error() << "Got a dude" << output->testLog(RTags::DiagnosticsLevel);
|
||||
if (output->testLog(RTags::DiagnosticsLevel)) {
|
||||
auto out = std::make_shared<Output>();
|
||||
out->output = output;
|
||||
if (output->flags() & RTagsLogOutput::Elisp) {
|
||||
out->flags |= CompletionThread::Elisp;
|
||||
elisp = true;
|
||||
} else if (output->flags() & RTagsLogOutput::XML) {
|
||||
out->flags |= CompletionThread::XML;
|
||||
xml = true;
|
||||
#ifdef HAS_JSON_H
|
||||
} else if (output->flags() & RTagsLogOutput::JSON) {
|
||||
out->flags |= CompletionThread::JSON;
|
||||
send_json = true;
|
||||
#endif
|
||||
} else {
|
||||
raw = true;
|
||||
}
|
||||
outputs.append(out);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!outputs.isEmpty()) {
|
||||
String rawOut, xmlOut, elispOut;
|
||||
#ifdef HAS_JSON_H
|
||||
String jsonOut;
|
||||
#endif
|
||||
if (raw)
|
||||
rawOut.reserve(16384);
|
||||
if (xml) {
|
||||
xmlOut.reserve(16384);
|
||||
xmlOut += String::format<128>("<?xml version=\"1.0\" encoding=\"utf-8\"?><completions location=\"%s\"><![CDATA[",
|
||||
request->location.toString(Location::AbsolutePath).constData());
|
||||
}
|
||||
if (elisp) {
|
||||
elispOut.reserve(16384);
|
||||
elispOut << String::format<256>("(list 'completions (list \"%s\" (list",
|
||||
RTags::elispEscape(request->location.toString(Location::AbsolutePath)).constData());
|
||||
}
|
||||
for (const std::unique_ptr<MatchResult> &result : results) {
|
||||
CompletionCandidate *c = result->candidate;
|
||||
const String str = String::format<128>(" %s %s %s %s %s %s\n",
|
||||
c->name.c_str(),
|
||||
c->signature.c_str(),
|
||||
c->kind.c_str(),
|
||||
c->annotation.c_str(),
|
||||
c->parent.c_str(),
|
||||
c->brief_comment.c_str());
|
||||
|
||||
|
||||
if (xml || raw)
|
||||
if (raw)
|
||||
rawOut += str;
|
||||
if (xml)
|
||||
xmlOut += str;
|
||||
#ifdef HAS_JSON_H
|
||||
if (send_json) {
|
||||
j["completions"] += {
|
||||
{"completion", c->name},
|
||||
{"signature", c->signature},
|
||||
{"kind", c->kind},
|
||||
{"parent", c->parent},
|
||||
{"brief_comment", c->brief_comment},
|
||||
{"annotation", c->annotation},
|
||||
{"priority", c->priority}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
if (elisp) {
|
||||
elispOut += String::format<128>(" (list \"%s\" \"%s\" \"%s\" \"%s\")",
|
||||
RTags::elispEscape(c->name).constData(),
|
||||
RTags::elispEscape(c->signature).constData(),
|
||||
c->kind.c_str(),
|
||||
RTags::elispEscape(c->brief_comment).constData());
|
||||
//,
|
||||
// RTags::elispEscape(val->annotation).constData(),
|
||||
// val->parent.constData(),
|
||||
// val->briefComment.constData());
|
||||
}
|
||||
}
|
||||
if (elisp)
|
||||
elispOut += ")))";
|
||||
if (xml)
|
||||
xmlOut += "]]></completions>\n";
|
||||
#ifdef HAS_JSON_H
|
||||
if (send_json)
|
||||
jsonOut = j.dump(4);
|
||||
#endif
|
||||
|
||||
EventLoop::mainEventLoop()->callLater([outputs, xmlOut, elispOut, rawOut
|
||||
#ifdef HAS_JSON_H
|
||||
, jsonOut
|
||||
#endif
|
||||
]() {
|
||||
for (auto &it : outputs) {
|
||||
if (it->flags & Elisp) {
|
||||
it->send(elispOut);
|
||||
} else if (it->flags & XML) {
|
||||
it->send(xmlOut);
|
||||
#ifdef HAS_JSON_H
|
||||
} else if (it->flags & JSON) {
|
||||
it->send(jsonOut);
|
||||
#endif
|
||||
} else {
|
||||
it->send(rawOut);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool CompletionThread::isCached(uint32_t fileId, const std::shared_ptr<Project> &project) const
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
for (SourceFile *file : mCacheList) {
|
||||
if (file->source.fileId == fileId || project->dependsOn(file->source.fileId, fileId))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
String CompletionThread::Request::toString() const
|
||||
{
|
||||
String ret = location.toString(Location::NoColor);
|
||||
if (!unsaved.isEmpty()) {
|
||||
ret += String::format<64>(" - Unsaved: %zu", unsaved.size());
|
||||
}
|
||||
|
||||
struct {
|
||||
const char *name;
|
||||
const Flag flag;
|
||||
} const f[] = {
|
||||
{ "Elisp", Elisp },
|
||||
{ "XML", XML },
|
||||
{ "JSON", JSON },
|
||||
{ "IncludeMacros", IncludeMacros },
|
||||
{ "WarmUp", WarmUp },
|
||||
};
|
||||
|
||||
for (const auto &flag : f) {
|
||||
if (flags & flag.flag) {
|
||||
ret += String::format<64>(" - %s", flag.name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!unsaved.isEmpty() && !location.isNull()) {
|
||||
int line = location.line();
|
||||
int pos = 0;
|
||||
while (line > 1) {
|
||||
int p = unsaved.indexOf('\n', pos);
|
||||
if (p == -1) {
|
||||
pos = -1;
|
||||
break;
|
||||
}
|
||||
pos = p + 1;
|
||||
--line;
|
||||
}
|
||||
if (pos != -1) {
|
||||
int end = unsaved.indexOf('\n', pos);
|
||||
if (end == -1)
|
||||
end = unsaved.size();
|
||||
ret += String::format<1024>(" - Completing at %s:%d:%d line: [%s]",
|
||||
location.path().constData(),
|
||||
location.line(),
|
||||
location.column(),
|
||||
unsaved.mid(pos, end - pos).constData());
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Source CompletionThread::findSource(const Set<uint32_t> &deps) const
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
for (SourceFile *sourceFile = mCacheList.first(); sourceFile; sourceFile = sourceFile->next) {
|
||||
if (deps.contains(sourceFile->source.fileId)) {
|
||||
return sourceFile->source;
|
||||
}
|
||||
}
|
||||
return Source();
|
||||
}
|
||||
|
||||
class CompletionDiagnostics : public RTags::DiagnosticsProvider
|
||||
{
|
||||
public:
|
||||
CompletionDiagnostics(uint32_t sourceFileId, uint32_t completionFileId, CXCodeCompleteResults *results, CXTranslationUnit unit)
|
||||
: mSourceFileId(sourceFileId), mResults(results), mUnit(unit)
|
||||
{
|
||||
mIndexDataMessage.files()[completionFileId] = IndexDataMessage::Visited;
|
||||
}
|
||||
|
||||
virtual size_t unitCount() const override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
virtual size_t diagnosticCount(size_t) const override
|
||||
{
|
||||
return clang_codeCompleteGetNumDiagnostics(mResults);
|
||||
}
|
||||
|
||||
virtual CXDiagnostic diagnostic(size_t, size_t idx) const override
|
||||
{
|
||||
return clang_codeCompleteGetDiagnostic(mResults, idx);
|
||||
}
|
||||
|
||||
virtual Location createLocation(const Path &file, unsigned int line, unsigned int col, bool *blocked = 0) override
|
||||
{
|
||||
if (blocked)
|
||||
*blocked = false;
|
||||
return Location(Location::insertFile(file), line, col);
|
||||
}
|
||||
|
||||
virtual uint32_t sourceFileId() const override
|
||||
{
|
||||
return mSourceFileId;
|
||||
}
|
||||
|
||||
virtual IndexDataMessage &indexDataMessage() override
|
||||
{
|
||||
return mIndexDataMessage;
|
||||
}
|
||||
|
||||
virtual CXTranslationUnit unit(size_t) const override
|
||||
{
|
||||
return mUnit;
|
||||
}
|
||||
|
||||
const uint32_t mSourceFileId;
|
||||
CXCodeCompleteResults *const mResults;
|
||||
CXTranslationUnit const mUnit;
|
||||
IndexDataMessage mIndexDataMessage;
|
||||
};
|
||||
|
||||
void CompletionThread::processDiagnostics(const Request *request, CXCodeCompleteResults *results, CXTranslationUnit unit)
|
||||
{
|
||||
assert(request);
|
||||
std::shared_ptr<Project> project = Server::instance()->currentProject();
|
||||
if (!project) {
|
||||
return;
|
||||
}
|
||||
const uint32_t sourceFileId = request->source.fileId;
|
||||
if (!project->hasSource(sourceFileId)) {
|
||||
return;
|
||||
}
|
||||
LOG() << "processing diagnostics" << clang_codeCompleteGetNumDiagnostics(results)
|
||||
<< clang_getNumDiagnostics(unit) << request->location << Location::path(request->source.fileId);
|
||||
CompletionDiagnostics diag(sourceFileId, request->location.fileId(), results, unit);
|
||||
diag.diagnose();
|
||||
// error() << "got diagnostics" << diag.indexDataMessage().diagnostics().size();
|
||||
Project::FileMapScopeScope scope(project);
|
||||
project->updateDiagnostics(sourceFileId, diag.indexDataMessage().diagnostics());
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef CompletionThread_h
|
||||
#define CompletionThread_h
|
||||
|
||||
#include <clang-c/Index.h>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "Location.h"
|
||||
#include "rct/Connection.h"
|
||||
#include "rct/EmbeddedLinkedList.h"
|
||||
#include "rct/Flags.h"
|
||||
#include "rct/LinkedList.h"
|
||||
#include "rct/Map.h"
|
||||
#include "rct/Thread.h"
|
||||
#include "Source.h"
|
||||
#include "RTags.h"
|
||||
|
||||
struct MatchResult;
|
||||
class CompletionThread : public Thread
|
||||
{
|
||||
public:
|
||||
CompletionThread(int cacheSize);
|
||||
~CompletionThread();
|
||||
|
||||
virtual void run() override;
|
||||
enum Flag {
|
||||
None = 0x00,
|
||||
Elisp = 0x01,
|
||||
XML = 0x02,
|
||||
JSON = 0x04,
|
||||
IncludeMacros = 0x08,
|
||||
WarmUp = 0x10,
|
||||
NoWait = 0x20
|
||||
};
|
||||
bool isCached(uint32_t fileId, const std::shared_ptr<Project> &project) const;
|
||||
void completeAt(Source &&source, Location location, Flags<Flag> flags,
|
||||
String &&unsaved, const String &prefix,
|
||||
const std::shared_ptr<Connection> &conn);
|
||||
void prepare(Source &&source, String &&unsaved);
|
||||
Source findSource(const Set<uint32_t> &deps) const;
|
||||
void stop();
|
||||
String dump();
|
||||
private:
|
||||
struct Request;
|
||||
void processDiagnostics(const Request *request, CXCodeCompleteResults *results, CXTranslationUnit unit);
|
||||
void process(Request *request);
|
||||
|
||||
Set<uint32_t> mWatched;
|
||||
bool mShutdown;
|
||||
const size_t mCacheSize;
|
||||
struct Request {
|
||||
~Request()
|
||||
{
|
||||
if (conn)
|
||||
conn->finish();
|
||||
}
|
||||
String toString() const;
|
||||
Source source;
|
||||
Location location;
|
||||
Flags<Flag> flags;
|
||||
String unsaved, prefix;
|
||||
std::shared_ptr<Connection> conn;
|
||||
};
|
||||
LinkedList<Request*> mPending;
|
||||
struct Dump {
|
||||
bool done;
|
||||
std::mutex mutex;
|
||||
std::condition_variable cond;
|
||||
String string;
|
||||
} *mDump;
|
||||
|
||||
struct Completions {
|
||||
Completions(Location loc) : location(loc), next(0), prev(0) {}
|
||||
struct Candidate {
|
||||
String completion, signature, annotation, parent, briefComment;
|
||||
int priority = 0;
|
||||
#ifdef RTAGS_COMPLETION_TOKENS_ENABLED
|
||||
int distance = 0;
|
||||
#endif
|
||||
CXCursorKind cursorKind;
|
||||
struct Chunk {
|
||||
Chunk()
|
||||
: kind(CXCompletionChunk_Optional)
|
||||
{}
|
||||
Chunk(String &&t, CXCompletionChunkKind k)
|
||||
: text(std::forward<String>(t)), kind(k)
|
||||
{}
|
||||
String text;
|
||||
CXCompletionChunkKind kind;
|
||||
};
|
||||
List<Chunk> chunks;
|
||||
|
||||
enum Flag {
|
||||
None = 0x0,
|
||||
IncludeChunks = 0x1
|
||||
};
|
||||
Value toValue(unsigned int flags) const;
|
||||
};
|
||||
|
||||
List<Candidate> candidates;
|
||||
const Location location;
|
||||
Flags<Flag> flags;
|
||||
Completions *next, *prev;
|
||||
};
|
||||
|
||||
void printCompletions(const List<std::unique_ptr<MatchResult> > &results, Request *request);
|
||||
static bool compareCompletionCandidates(const Completions::Candidate *l,
|
||||
const Completions::Candidate *r);
|
||||
|
||||
struct SourceFile {
|
||||
SourceFile()
|
||||
: lastModified(0), parseTime(0), reparseTime(0), codeCompleteTime(0), completions(0), next(0), prev(0)
|
||||
{}
|
||||
std::shared_ptr<RTags::TranslationUnit> translationUnit;
|
||||
String unsaved;
|
||||
uint64_t lastModified;
|
||||
uint64_t parseTime, reparseTime, codeCompleteTime; // ms
|
||||
size_t completions;
|
||||
Source source;
|
||||
SourceFile *next, *prev;
|
||||
};
|
||||
|
||||
#ifdef RTAGS_COMPLETION_TOKENS_ENABLED
|
||||
struct Token
|
||||
{
|
||||
Token(const char *bytes = 0, int size = 0)
|
||||
: data(bytes), length(size)
|
||||
{}
|
||||
|
||||
inline bool operator==(const Token &other) const
|
||||
{
|
||||
return length == other.length && !strncmp(data, other.data, length);
|
||||
}
|
||||
inline bool operator<(const Token &other) const
|
||||
{
|
||||
if (!data)
|
||||
return !other.data ? 0 : -1;
|
||||
if (!other.data)
|
||||
return 1;
|
||||
const int minLength = std::min(length, other.length);
|
||||
int ret = memcmp(data, other.data, minLength);
|
||||
if (!ret) {
|
||||
if (length < other.length) {
|
||||
ret = -1;
|
||||
} else if (other.length < length) {
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *data;
|
||||
int length;
|
||||
|
||||
static inline Map<Token, int> tokenize(const char *data, int size)
|
||||
{
|
||||
Map<Token, int> tokens;
|
||||
int tokenEnd = -1;
|
||||
for (int i=size - 1; i>=0; --i) {
|
||||
if (RTags::isSymbol(data[i])) {
|
||||
if (tokenEnd == -1)
|
||||
tokenEnd = i;
|
||||
} else if (tokenEnd != -1) {
|
||||
addToken(data, i + 1, tokenEnd - i, tokens);
|
||||
tokenEnd = -1;
|
||||
}
|
||||
}
|
||||
if (tokenEnd != -1)
|
||||
addToken(data, 0, tokenEnd + 1, tokens);
|
||||
return tokens;
|
||||
}
|
||||
private:
|
||||
static inline void addToken(const char *data, int pos, int len, Map<Token, int> &tokens)
|
||||
{
|
||||
int &val = tokens[Token(data + pos, len)];
|
||||
if (!val)
|
||||
val = pos;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
Hash<uint32_t, SourceFile*> mCacheMap;
|
||||
EmbeddedLinkedList<SourceFile*> mCacheList;
|
||||
|
||||
mutable std::mutex mMutex;
|
||||
std::condition_variable mCondition;
|
||||
};
|
||||
|
||||
RCT_FLAGS(CompletionThread::Flag);
|
||||
|
||||
#endif
|
||||
@@ -1,40 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "DependenciesJob.h"
|
||||
|
||||
#include "FileManager.h"
|
||||
#include "Project.h"
|
||||
#include "QueryMessage.h"
|
||||
#include "RTags.h"
|
||||
#include "Server.h"
|
||||
|
||||
DependenciesJob::DependenciesJob(const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Project> &project)
|
||||
: QueryJob(query, project, QuietJob)
|
||||
{
|
||||
Deserializer deserializer(query->query());
|
||||
Path path;
|
||||
deserializer >> path >> mArgs;
|
||||
mFileId = Location::fileId(path);
|
||||
}
|
||||
|
||||
int DependenciesJob::execute()
|
||||
{
|
||||
std::shared_ptr<Project> proj = project();
|
||||
if (!proj)
|
||||
return 2;
|
||||
write(proj->dumpDependencies(mFileId, mArgs, queryFlags()));
|
||||
return 0;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef DependenciesJob_h
|
||||
#define DependenciesJob_h
|
||||
|
||||
#include "QueryJob.h"
|
||||
|
||||
|
||||
class Project;
|
||||
class QueryMessage;
|
||||
class DependenciesJob : public QueryJob
|
||||
{
|
||||
public:
|
||||
DependenciesJob(const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Project> &project);
|
||||
protected:
|
||||
virtual int execute() override;
|
||||
private:
|
||||
uint32_t mFileId;
|
||||
List<String> mArgs;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,99 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef Diagnostic_h
|
||||
#define Diagnostic_h
|
||||
|
||||
#include "rct/Serializer.h"
|
||||
#include "rct/String.h"
|
||||
#include "Location.h"
|
||||
#include "Sandbox.h"
|
||||
|
||||
struct Diagnostic;
|
||||
typedef Map<Location, Diagnostic> Diagnostics;
|
||||
|
||||
struct Diagnostic
|
||||
{
|
||||
enum Flag {
|
||||
None = 0x00,
|
||||
Warning = 0x01,
|
||||
Error = 0x02,
|
||||
Fixit = 0x04,
|
||||
Note = 0x08,
|
||||
Skipped = 0x10,
|
||||
Type_Mask = Warning|Error|Fixit|Note|Skipped,
|
||||
TemplateOnly = 0x20,
|
||||
DisplayCategory = 0x40
|
||||
};
|
||||
|
||||
Diagnostic()
|
||||
: length(-1), sourceFileId(0)
|
||||
{
|
||||
}
|
||||
|
||||
Flag type() const;
|
||||
|
||||
String message;
|
||||
int length;
|
||||
uint32_t sourceFileId;
|
||||
|
||||
Map<Location, int> ranges;
|
||||
Diagnostics children;
|
||||
Flags<Flag> flags;
|
||||
bool isNull() const { return type() == None; }
|
||||
|
||||
bool operator==(const Diagnostic &other) const
|
||||
{
|
||||
return (message == other.message
|
||||
&& length == other.length
|
||||
&& sourceFileId == other.sourceFileId
|
||||
&& ranges == other.ranges
|
||||
&& children == other.children
|
||||
&& flags == other.flags);
|
||||
}
|
||||
|
||||
bool operator!=(const Diagnostic &other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
};
|
||||
|
||||
RCT_FLAGS(Diagnostic::Flag);
|
||||
|
||||
inline Diagnostic::Flag Diagnostic::type() const
|
||||
{
|
||||
return Flags<Flag>(flags & Type_Mask).cast<Flag>();
|
||||
}
|
||||
|
||||
template <> inline Serializer &operator<<(Serializer &s, const Diagnostic &d)
|
||||
{
|
||||
// SBROOT
|
||||
String tmessage = Sandbox::encoded(d.message);
|
||||
s << d.flags.cast<uint8_t>() << tmessage << d.length << d.sourceFileId << d.ranges << d.children;
|
||||
return s;
|
||||
}
|
||||
|
||||
template <> inline Deserializer &operator>>(Deserializer &s, Diagnostic &d)
|
||||
{
|
||||
uint8_t flags;
|
||||
String tmessage;
|
||||
s >> flags >> tmessage >> d.length >> d.sourceFileId >> d.ranges >> d.children;
|
||||
// SBROOT
|
||||
d.message = Sandbox::decoded(std::move(tmessage));
|
||||
d.flags = static_cast<Diagnostic::Flag>(flags);
|
||||
return s;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,177 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "FileManager.h"
|
||||
|
||||
#include "Filter.h"
|
||||
#include "Project.h"
|
||||
#include "ScanThread.h"
|
||||
#include "Server.h"
|
||||
|
||||
FileManager::FileManager(const std::shared_ptr<Project> &project)
|
||||
: mProject(project), mLastReloadTime(0)
|
||||
{
|
||||
}
|
||||
|
||||
void FileManager::load(Mode mode)
|
||||
{
|
||||
if (!Server::instance()->options().tests.isEmpty())
|
||||
mode = Synchronous;
|
||||
|
||||
mLastReloadTime = Rct::monoMs();
|
||||
std::shared_ptr<Project> project = mProject.lock();
|
||||
assert(project);
|
||||
if (mode == Asynchronous) {
|
||||
startScanThread();
|
||||
} else {
|
||||
const Set<Path> paths = ScanThread::paths(project->path());
|
||||
onRecurseJobFinished(paths);
|
||||
}
|
||||
}
|
||||
|
||||
void FileManager::onRecurseJobFinished(const Set<Path> &paths)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex); // ### is this needed now?
|
||||
|
||||
std::shared_ptr<Project> project = mProject.lock();
|
||||
if (!project)
|
||||
return;
|
||||
Files &map = project->files();
|
||||
map.clear();
|
||||
clearFileSystemWatcher();
|
||||
for (Set<Path>::const_iterator it = paths.begin(); it != paths.end(); ++it) {
|
||||
const Path parent = it->parentDir();
|
||||
if (parent.isEmpty()) {
|
||||
error() << "Got empty parent here" << *it;
|
||||
continue;
|
||||
}
|
||||
assert(!parent.isEmpty());
|
||||
Set<String> &dir = map[parent];
|
||||
if (dir.isEmpty()) {
|
||||
watch(parent);
|
||||
// error() << "Watching parent" << parent;
|
||||
}
|
||||
dir.insert(it->fileName());
|
||||
}
|
||||
assert(!map.contains(Path()));
|
||||
}
|
||||
|
||||
void FileManager::onFileAdded(const Path &path)
|
||||
{
|
||||
debug() << "fm file added" << path;
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
const Filter::Result res = Filter::filter(path);
|
||||
switch (res) {
|
||||
case Filter::Directory:
|
||||
watch(path);
|
||||
load(Asynchronous);
|
||||
return;
|
||||
case Filter::Filtered:
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
std::shared_ptr<Project> project = mProject.lock();
|
||||
assert(project);
|
||||
Files &map = project->files();
|
||||
const Path parent = path.parentDir();
|
||||
if (!parent.isEmpty()) {
|
||||
Set<String> &dir = map[parent];
|
||||
watch(parent);
|
||||
dir.insert(path.fileName());
|
||||
} else {
|
||||
error() << "Got empty parent here" << path;
|
||||
load(Asynchronous);
|
||||
}
|
||||
assert(!map.contains(Path()));
|
||||
}
|
||||
|
||||
void FileManager::onFileRemoved(const Path &path)
|
||||
{
|
||||
debug() << "fm file removed" << path;
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
std::shared_ptr<Project> project = mProject.lock();
|
||||
if (!project)
|
||||
return;
|
||||
Files &map = project->files();
|
||||
if (!map.remove(path)) {
|
||||
const Path parent = path.parentDir();
|
||||
if (map.contains(parent)) {
|
||||
Set<String> &dir = map[parent];
|
||||
dir.remove(String(path.fileName()));
|
||||
if (dir.isEmpty()) {
|
||||
project->unwatch(parent, Project::Watch_FileManager);
|
||||
map.remove(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool startsWith(const Path &left, const Path &right)
|
||||
{
|
||||
assert(!left.isEmpty());
|
||||
return !right.isEmpty() && left.startsWith(right);
|
||||
}
|
||||
|
||||
bool FileManager::contains(const Path &path) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
std::shared_ptr<Project> proj = mProject.lock();
|
||||
if (!proj)
|
||||
return false;
|
||||
if (startsWith(path, proj->path()))
|
||||
return true;
|
||||
const Path p = Path::resolved(path);
|
||||
if (p != path && startsWith(path, proj->path()))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void FileManager::watch(const Path &path)
|
||||
{
|
||||
if (Server::instance()->options().options & Server::NoFileManagerWatch)
|
||||
return;
|
||||
if (path.contains("/.git/") || path.contains("/.svn/") || path.contains("/.cvs/")) {
|
||||
return; // more source control systems?
|
||||
}
|
||||
if (auto proj = mProject.lock()) {
|
||||
proj->watch(path, Project::Watch_FileManager);
|
||||
}
|
||||
}
|
||||
|
||||
void FileManager::startScanThread()
|
||||
{
|
||||
std::shared_ptr<Project> project = mProject.lock();
|
||||
assert(project);
|
||||
ScanThread *thread = new ScanThread(project->path());
|
||||
thread->setAutoDelete(true);
|
||||
std::weak_ptr<FileManager> that = shared_from_this();
|
||||
thread->finished().connect<EventLoop::Move>([that](const Set<Path> &paths) {
|
||||
if (auto strong = that.lock())
|
||||
strong->onRecurseJobFinished(paths);
|
||||
});
|
||||
|
||||
thread->start();
|
||||
}
|
||||
|
||||
void FileManager::clearFileSystemWatcher()
|
||||
{
|
||||
if (auto project = mProject.lock())
|
||||
project->clearWatch(Project::Watch_FileManager);
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef FileManager_h
|
||||
#define FileManager_h
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "rct/Path.h"
|
||||
#include "rct/Timer.h"
|
||||
|
||||
class Project;
|
||||
class FileManager : public std::enable_shared_from_this<FileManager>
|
||||
{
|
||||
public:
|
||||
FileManager(const std::shared_ptr<Project> &project);
|
||||
enum Mode {
|
||||
Synchronous,
|
||||
Asynchronous
|
||||
};
|
||||
|
||||
void load(Mode mode);
|
||||
uint64_t lastReloadTime() const { return mLastReloadTime; }
|
||||
void onFileAdded(const Path &path);
|
||||
void onFileRemoved(const Path &path);
|
||||
void onRecurseJobFinished(const Set<Path> &mPaths);
|
||||
bool contains(const Path &path) const;
|
||||
void clearFileSystemWatcher();
|
||||
private:
|
||||
void startScanThread();
|
||||
void watch(const Path &path);
|
||||
std::weak_ptr<Project> mProject;
|
||||
uint64_t mLastReloadTime;
|
||||
mutable std::mutex mMutex;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,314 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef FileMap_h
|
||||
#define FileMap_h
|
||||
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
|
||||
#include "Location.h"
|
||||
#include "rct/Serializer.h"
|
||||
|
||||
template <typename T> inline static int compare(const T &l, const T &r)
|
||||
{
|
||||
if (l < r)
|
||||
return -1;
|
||||
if (l > r)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <> inline int compare(const String &l, const String &r)
|
||||
{
|
||||
return l.compare(r);
|
||||
}
|
||||
|
||||
template <> inline int compare(const Location &l, const Location &r)
|
||||
{
|
||||
return l.compare(r);
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
class FileMap
|
||||
{
|
||||
public:
|
||||
FileMap()
|
||||
: mPointer(0), mSize(0), mCount(0), mValuesOffset(0), mFD(-1), mOptions(0)
|
||||
{}
|
||||
|
||||
~FileMap()
|
||||
{
|
||||
if (mFD != -1) {
|
||||
assert(mPointer);
|
||||
munmap(const_cast<char*>(mPointer), mSize);
|
||||
if (!(mOptions & NoLock))
|
||||
lock(mFD, Unlock);
|
||||
int ret;
|
||||
eintrwrap(ret, close(mFD));
|
||||
}
|
||||
}
|
||||
|
||||
void init(const char *pointer, uint32_t size)
|
||||
{
|
||||
mPointer = pointer;
|
||||
mSize = size;
|
||||
memcpy(&mCount, mPointer, sizeof(uint32_t));
|
||||
memcpy(&mValuesOffset, mPointer + sizeof(uint32_t), sizeof(uint32_t));
|
||||
}
|
||||
|
||||
enum Options {
|
||||
None = 0x0,
|
||||
NoLock = 0x1
|
||||
};
|
||||
bool load(const Path &path, uint32_t options, String *error = 0)
|
||||
{
|
||||
eintrwrap(mFD, open(path.constData(), O_RDONLY));
|
||||
if (mFD == -1) {
|
||||
if (error) {
|
||||
*error = Rct::strerror();
|
||||
*error << " " << __LINE__;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!(options & NoLock) && !lock(mFD, Read)) {
|
||||
if (error) {
|
||||
*error = Rct::strerror();
|
||||
*error << " " << __LINE__;
|
||||
}
|
||||
|
||||
close(mFD);
|
||||
mFD = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (fstat(mFD, &st)) {
|
||||
if (error) {
|
||||
*error = Rct::strerror();
|
||||
*error << " " << __LINE__;
|
||||
}
|
||||
lock(mFD, Unlock);
|
||||
int ret;
|
||||
eintrwrap(ret, close(mFD));
|
||||
mFD = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *pointer = static_cast<const char*>(mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, mFD, 0));
|
||||
// error() << errno;// << mPointer;
|
||||
if (pointer == MAP_FAILED) {
|
||||
if (error) {
|
||||
*error = Rct::strerror();
|
||||
*error << " " << __LINE__;
|
||||
}
|
||||
lock(mFD, Unlock);
|
||||
int ret;
|
||||
eintrwrap(ret, close(mFD));
|
||||
mFD = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
mOptions = options;
|
||||
init(pointer, st.st_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
Value value(const Key &key, bool *matched = 0) const
|
||||
{
|
||||
bool match;
|
||||
const uint32_t idx = lowerBound(key, &match);
|
||||
// error() << "value" << idx << key << match;
|
||||
if (matched)
|
||||
*matched = match;
|
||||
if (match)
|
||||
return valueAt(idx);
|
||||
return Value();
|
||||
}
|
||||
|
||||
uint32_t count() const { return mCount; }
|
||||
|
||||
Key keyAt(uint32_t index) const
|
||||
{
|
||||
assert(index >= 0 && index < mCount);
|
||||
return read<Key>(keysSegment(), index);
|
||||
}
|
||||
|
||||
Value valueAt(uint32_t index) const
|
||||
{
|
||||
assert(index >= 0 && index < mCount);
|
||||
return read<Value>(valuesSegment(), index);
|
||||
}
|
||||
|
||||
uint32_t lowerBound(const Key &k, bool *match = 0) const
|
||||
{
|
||||
if (!mCount) {
|
||||
if (match)
|
||||
*match = false;
|
||||
return std::numeric_limits<uint32_t>::max();
|
||||
|
||||
}
|
||||
int lower = 0;
|
||||
int upper = mCount - 1;
|
||||
|
||||
do {
|
||||
const int mid = lower + ((upper - lower) / 2);
|
||||
const int cmp = compare<Key>(k, keyAt(mid));
|
||||
if (cmp < 0) {
|
||||
upper = mid - 1;
|
||||
} else if (cmp > 0) {
|
||||
lower = mid + 1;
|
||||
} else {
|
||||
if (match)
|
||||
*match = true;
|
||||
return mid;
|
||||
}
|
||||
} while (lower <= upper);
|
||||
|
||||
if (lower == static_cast<int>(mCount))
|
||||
lower = std::numeric_limits<uint32_t>::max();
|
||||
if (match)
|
||||
*match = false;
|
||||
return lower;
|
||||
}
|
||||
|
||||
static String encode(const Map<Key, Value> &map)
|
||||
{
|
||||
String out;
|
||||
Serializer serializer(out);
|
||||
serializer << static_cast<uint32_t>(map.size());
|
||||
uint32_t valuesOffset;
|
||||
if (uint32_t size = FixedSize<Key>::value) {
|
||||
valuesOffset = ((static_cast<uint32_t>(map.size()) * size) + (sizeof(uint32_t) * 2));
|
||||
serializer << valuesOffset;
|
||||
for (const std::pair<Key, Value> &pair : map) {
|
||||
out.append(reinterpret_cast<const char*>(&pair.first), size);
|
||||
}
|
||||
} else {
|
||||
serializer << static_cast<uint32_t>(0); // values offset
|
||||
uint32_t offset = sizeof(uint32_t) * 2 + (map.size() * sizeof(uint32_t));
|
||||
String keyData;
|
||||
Serializer keySerializer(keyData);
|
||||
for (const std::pair<Key, Value> &pair : map) {
|
||||
const uint32_t pos = offset + keyData.size();
|
||||
out.append(reinterpret_cast<const char*>(&pos), sizeof(pos));
|
||||
keySerializer << pair.first;
|
||||
}
|
||||
out.append(keyData);
|
||||
valuesOffset = out.size();
|
||||
memcpy(out.data() + sizeof(uint32_t), &valuesOffset, sizeof(valuesOffset));
|
||||
}
|
||||
assert(valuesOffset == static_cast<uint32_t>(out.size()));
|
||||
|
||||
if (uint32_t size = FixedSize<Value>::value) {
|
||||
for (const std::pair<Key, Value> &pair : map) {
|
||||
out.append(reinterpret_cast<const char*>(&pair.second), size);
|
||||
}
|
||||
} else {
|
||||
const uint32_t encodedValuesOffset = valuesOffset + (sizeof(uint32_t) * map.size());
|
||||
String valueData;
|
||||
Serializer valueSerializer(valueData);
|
||||
for (const std::pair<Key, Value> &pair : map) {
|
||||
const uint32_t pos = encodedValuesOffset + valueData.size();
|
||||
out.append(reinterpret_cast<const char*>(&pos), sizeof(pos));
|
||||
valueSerializer << pair.second;
|
||||
}
|
||||
out.append(valueData);
|
||||
|
||||
}
|
||||
return out;
|
||||
}
|
||||
static size_t write(const Path &path, const Map<Key, Value> &map, uint32_t options)
|
||||
{
|
||||
int fd = open(path.constData(), O_RDWR|O_CREAT, 0644);
|
||||
if (fd == -1) {
|
||||
if (!Path::mkdir(path.parentDir(), Path::Recursive))
|
||||
return 0;
|
||||
fd = open(path.constData(), O_RDWR|O_CREAT, 0644);
|
||||
if (fd == -1)
|
||||
return 0;
|
||||
}
|
||||
if (!(options & NoLock) && !lock(fd, Write)) {
|
||||
::close(fd);
|
||||
return 0;
|
||||
}
|
||||
const String data = encode(map);
|
||||
bool ok = ::ftruncate(fd, data.size()) != -1;
|
||||
if (!ok) {
|
||||
if (!(options & NoLock))
|
||||
lock(fd, Unlock);
|
||||
::close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ok = ::write(fd, data.constData(), data.size()) == static_cast<ssize_t>(data.size());
|
||||
if (!(options & NoLock))
|
||||
ok = lock(fd, Unlock) && ok;
|
||||
|
||||
::close(fd);
|
||||
if (!ok)
|
||||
unlink(path.constData());
|
||||
return ok ? data.size() : 0;
|
||||
}
|
||||
private:
|
||||
enum Mode {
|
||||
Read = F_RDLCK,
|
||||
Write = F_WRLCK,
|
||||
Unlock = F_UNLCK
|
||||
};
|
||||
static bool lock(int fd, Mode mode)
|
||||
{
|
||||
struct flock fl;
|
||||
memset(&fl, 0, sizeof(fl));
|
||||
fl.l_type = mode;
|
||||
fl.l_whence = SEEK_SET;
|
||||
fl.l_pid = getpid();
|
||||
int ret;
|
||||
eintrwrap(ret, fcntl(fd, F_SETLKW, &fl));
|
||||
return ret != -1;
|
||||
}
|
||||
const char *valuesSegment() const { return mPointer + mValuesOffset; }
|
||||
const char *keysSegment() const { return mPointer + (sizeof(uint32_t) * 2); }
|
||||
|
||||
template <typename T>
|
||||
inline T read(const char *base, uint32_t index) const
|
||||
{
|
||||
if (const uint32_t size = FixedSize<T>::value) {
|
||||
T t = T();
|
||||
memcpy(&t, base + (index * size), FixedSize<T>::value);
|
||||
return t;
|
||||
}
|
||||
uint32_t offset;
|
||||
memcpy(&offset, base + (sizeof(uint32_t) * index), sizeof(offset));
|
||||
Deserializer deserializer(mPointer + offset, INT_MAX);
|
||||
T t;
|
||||
deserializer >> t;
|
||||
return t;
|
||||
}
|
||||
|
||||
const char *mPointer;
|
||||
uint32_t mSize;
|
||||
uint32_t mCount;
|
||||
uint32_t mValuesOffset;
|
||||
int mFD;
|
||||
uint32_t mOptions;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,52 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef Filter_h
|
||||
#define Filter_h
|
||||
|
||||
#include <fnmatch.h>
|
||||
|
||||
#include "rct/List.h"
|
||||
#include "rct/Path.h"
|
||||
#include "rct/String.h"
|
||||
|
||||
namespace Filter {
|
||||
enum Result {
|
||||
Filtered,
|
||||
File,
|
||||
Source,
|
||||
Directory
|
||||
};
|
||||
|
||||
static inline Result filter(const Path &path, const List<String> &filters = List<String>())
|
||||
{
|
||||
const int size = filters.size();
|
||||
for (int i=0; i<size; ++i) {
|
||||
const String &filter = filters.at(i);
|
||||
if (!fnmatch(filter.constData(), path.constData(), 0) || path.contains(filter))
|
||||
return Filtered;
|
||||
}
|
||||
|
||||
if (path.isDir())
|
||||
return Directory;
|
||||
|
||||
const char *ext = path.extension();
|
||||
if (ext && (Path::isSource(ext) || Path::isHeader(ext)))
|
||||
return Source;
|
||||
return File;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,170 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "FindFileJob.h"
|
||||
|
||||
#include "FileManager.h"
|
||||
#include "Project.h"
|
||||
#include "rct/SignalSlot.h"
|
||||
#include "RTags.h"
|
||||
#include "Server.h"
|
||||
|
||||
static Flags<QueryJob::JobFlag> flags(Flags<QueryMessage::Flag> queryFlags)
|
||||
{
|
||||
Flags<QueryJob::JobFlag> flags = QueryJob::QuietJob;
|
||||
if (queryFlags & QueryMessage::Elisp)
|
||||
flags |= QueryJob::QuoteOutput;
|
||||
return flags;
|
||||
}
|
||||
|
||||
FindFileJob::FindFileJob(const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Project> &project)
|
||||
: QueryJob(query, project, ::flags(query->flags()))
|
||||
{
|
||||
const String q = query->query();
|
||||
if (!q.isEmpty()) {
|
||||
if (query->flags() & QueryMessage::MatchRegex) {
|
||||
mRegex = q.ref();
|
||||
} else {
|
||||
mPattern = q;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int FindFileJob::execute()
|
||||
{
|
||||
std::shared_ptr<Project> proj = project();
|
||||
if (!proj) {
|
||||
return 1;
|
||||
}
|
||||
if (!proj->fileManager())
|
||||
return 1;
|
||||
const Path srcRoot = proj->path();
|
||||
assert(srcRoot.endsWith('/'));
|
||||
|
||||
enum Mode {
|
||||
All,
|
||||
FilePath,
|
||||
Regex,
|
||||
Pattern,
|
||||
} mode = All;
|
||||
|
||||
String::CaseSensitivity cs = String::CaseSensitive;
|
||||
if (queryFlags() & QueryMessage::MatchRegex) {
|
||||
mode = Regex;
|
||||
} else if (!mPattern.isEmpty()) {
|
||||
mode = mPattern[0] == '/' ? FilePath : Pattern;
|
||||
}
|
||||
if (queryFlags() & QueryMessage::MatchCaseInsensitive)
|
||||
cs = String::CaseInsensitive;
|
||||
|
||||
String out;
|
||||
out.reserve(PATH_MAX);
|
||||
const bool absolutePath = queryFlags() & QueryMessage::AbsolutePath;
|
||||
if (absolutePath)
|
||||
out.append(srcRoot);
|
||||
const Files& dirs = proj->files();
|
||||
assert(proj->fileManager());
|
||||
if (dirs.isEmpty())
|
||||
proj->fileManager()->load(FileManager::Synchronous);
|
||||
Files::const_iterator dirit = dirs.begin();
|
||||
bool foundExact = false;
|
||||
const int patternSize = mPattern.size();
|
||||
List<String> matches;
|
||||
const bool preferExact = queryFlags() & QueryMessage::FindFilePreferExact;
|
||||
int ret = 1;
|
||||
bool firstElisp = queryFlags() & QueryMessage::Elisp;
|
||||
|
||||
auto writeFile = [this, &firstElisp](const Path &path) {
|
||||
if (firstElisp) {
|
||||
firstElisp = false;
|
||||
if (!write("(list", DontQuote))
|
||||
return false;
|
||||
}
|
||||
return write(path);
|
||||
};
|
||||
while (dirit != dirs.end()) {
|
||||
const Path &dir = dirit->first;
|
||||
if (dir.size() < srcRoot.size()) {
|
||||
continue;
|
||||
} else {
|
||||
out.append(dir.constData() + srcRoot.size(), dir.size() - srcRoot.size());
|
||||
}
|
||||
|
||||
const Set<String> &files = dirit->second;
|
||||
for (Set<String>::const_iterator it = files.begin(); it != files.end(); ++it) {
|
||||
const String &key = *it;
|
||||
out.append(key);
|
||||
bool ok = false;
|
||||
switch (mode) {
|
||||
case All:
|
||||
ok = true;
|
||||
break;
|
||||
case Regex:
|
||||
ok = Rct::contains(out, mRegex);
|
||||
break;
|
||||
case FilePath:
|
||||
case Pattern:
|
||||
if (!preferExact) {
|
||||
ok = out.contains(mPattern, cs);
|
||||
} else {
|
||||
const int outSize = out.size();
|
||||
const bool exact = (outSize > patternSize && out.endsWith(mPattern) && out.at(outSize - (patternSize + 1)) == '/');
|
||||
if (exact) {
|
||||
ok = true;
|
||||
if (!foundExact) {
|
||||
matches.clear();
|
||||
foundExact = true;
|
||||
}
|
||||
} else {
|
||||
ok = !foundExact && out.contains(mPattern, cs);
|
||||
}
|
||||
}
|
||||
if (!ok && mode == FilePath) {
|
||||
Path p(out);
|
||||
if (!absolutePath)
|
||||
p.prepend(srcRoot);
|
||||
p.resolve();
|
||||
if (p == mPattern)
|
||||
ok = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (ok) {
|
||||
ret = 0;
|
||||
|
||||
Path matched = out;
|
||||
if (absolutePath)
|
||||
matched.resolve();
|
||||
if (preferExact && !foundExact) {
|
||||
matches.append(matched);
|
||||
} else {
|
||||
if (!writeFile(matched))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
out.chop(key.size());
|
||||
}
|
||||
out.chop(dir.size() - srcRoot.size());
|
||||
++dirit;
|
||||
}
|
||||
for (List<String>::const_iterator it = matches.begin(); it != matches.end(); ++it) {
|
||||
if (!writeFile(*it)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (queryFlags() & QueryMessage::Elisp && !firstElisp && !write(")", DontQuote))
|
||||
return 1;
|
||||
return ret;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef FindFileJob_h
|
||||
#define FindFileJob_h
|
||||
|
||||
#include <regex>
|
||||
|
||||
#include "QueryJob.h"
|
||||
#include "rct/String.h"
|
||||
|
||||
class FindFileJob : public QueryJob
|
||||
{
|
||||
public:
|
||||
FindFileJob(const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Project> &project);
|
||||
protected:
|
||||
virtual int execute() override;
|
||||
private:
|
||||
String mPattern;
|
||||
std::regex mRegex;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,67 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "FindSymbolsJob.h"
|
||||
|
||||
#include "Project.h"
|
||||
#include "QueryMessage.h"
|
||||
#include "rct/Log.h"
|
||||
#include "RTags.h"
|
||||
#include "Server.h"
|
||||
|
||||
static inline Flags<QueryJob::JobFlag> jobFlags(Flags<QueryMessage::Flag> queryFlags)
|
||||
{
|
||||
return (queryFlags & QueryMessage::Elisp
|
||||
? Flags<QueryJob::JobFlag>(QueryJob::QuoteOutput|QueryJob::QuietJob)
|
||||
: Flags<QueryJob::JobFlag>(QueryJob::QuietJob));
|
||||
}
|
||||
|
||||
FindSymbolsJob::FindSymbolsJob(const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Project> &proj)
|
||||
: QueryJob(query, proj, ::jobFlags(query->flags())), string(query->query())
|
||||
{
|
||||
}
|
||||
|
||||
int FindSymbolsJob::execute()
|
||||
{
|
||||
int ret = 2;
|
||||
if (std::shared_ptr<Project> proj = project()) {
|
||||
Set<Symbol> symbols;
|
||||
auto inserter = [proj, this, &symbols](Project::SymbolMatchType type,
|
||||
const String &symbolName,
|
||||
const Set<Location> &locations) {
|
||||
if (type == Project::StartsWith) {
|
||||
const size_t paren = symbolName.indexOf('(');
|
||||
if (paren == String::npos || paren != string.size() || RTags::isFunctionVariable(symbolName))
|
||||
return;
|
||||
}
|
||||
for (const auto &it : locations) {
|
||||
const Symbol sym = proj->findSymbol(it);
|
||||
if (!sym.isNull() || sym.flags & Symbol::FileSymbol)
|
||||
symbols.insert(sym);
|
||||
}
|
||||
};
|
||||
proj->findSymbols(string, inserter, queryFlags(), fileFilter());
|
||||
if (!symbols.isEmpty()) {
|
||||
const List<RTags::SortedSymbol> sorted = proj->sort(symbols, queryFlags());
|
||||
const Flags<WriteFlag> writeFlags = fileFilter() ? Unfiltered : NoWriteFlags;
|
||||
const int count = sorted.size();
|
||||
ret = count ? 0 : 1;
|
||||
for (int i=0; i<count; ++i) {
|
||||
write(sorted.at(i).location, writeFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef FindSymbolsJob_h
|
||||
#define FindSymbolsJob_h
|
||||
|
||||
#include "QueryJob.h"
|
||||
#include "rct/String.h"
|
||||
|
||||
class QueryMessage;
|
||||
class FindSymbolsJob : public QueryJob
|
||||
{
|
||||
public:
|
||||
FindSymbolsJob(const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Project> &project);
|
||||
protected:
|
||||
virtual int execute() override;
|
||||
private:
|
||||
const String string;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,63 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef FixIt_h
|
||||
#define FixIt_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "rct/Serializer.h"
|
||||
#include "rct/String.h"
|
||||
|
||||
struct FixIt
|
||||
{
|
||||
inline FixIt(uint32_t l = 0, uint32_t c = 0, uint32_t len = 0, const String &t = String())
|
||||
: line(l), column(c), length(len), text(t)
|
||||
{
|
||||
}
|
||||
inline bool operator<(const FixIt &other) const
|
||||
{
|
||||
if (line < other.line)
|
||||
return true;
|
||||
if (line > other.line)
|
||||
return false;
|
||||
if (column < other.column)
|
||||
return true;
|
||||
if (column > other.column)
|
||||
return false;
|
||||
return length < other.length;
|
||||
}
|
||||
inline bool operator==(const FixIt &other) const
|
||||
{
|
||||
return (line == other.line && column == other.column && length == other.length && text == other.text);
|
||||
}
|
||||
|
||||
uint32_t line, column, length;
|
||||
String text;
|
||||
};
|
||||
|
||||
template <> inline Serializer &operator<<(Serializer &s, const FixIt &f)
|
||||
{
|
||||
s << f.line << f.column << f.length << f.text;
|
||||
return s;
|
||||
}
|
||||
|
||||
template <> inline Deserializer &operator>>(Deserializer &s, FixIt &f)
|
||||
{
|
||||
s >> f.line >> f.column >> f.length >> f.text;
|
||||
return s;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,110 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "FollowLocationJob.h"
|
||||
|
||||
#include "Project.h"
|
||||
#include "rct/SignalSlot.h"
|
||||
#include "RTags.h"
|
||||
#include "Server.h"
|
||||
|
||||
FollowLocationJob::FollowLocationJob(Location loc,
|
||||
const std::shared_ptr<QueryMessage> &query,
|
||||
const std::shared_ptr<Project> &project)
|
||||
: QueryJob(query, project), location(loc)
|
||||
{
|
||||
}
|
||||
|
||||
int FollowLocationJob::execute()
|
||||
{
|
||||
int idx = 0;
|
||||
Symbol symbol = project()->findSymbol(location, &idx);
|
||||
if (symbol.isNull()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// if you invoke a destructor explicitly there's a typeref on the class
|
||||
// name. This finds the destructor instead.
|
||||
if ((symbol.kind == CXCursor_TypeRef || symbol.kind == CXCursor_TemplateRef) && idx > 0) {
|
||||
auto symbols = project()->openSymbols(location.fileId());
|
||||
if (!symbols || !symbols->count())
|
||||
return 1;
|
||||
const Symbol prev = symbols->valueAt(idx - 1);
|
||||
if (prev.kind == CXCursor_MemberRefExpr
|
||||
&& prev.location.column() == symbol.location.column() - 1
|
||||
&& prev.location.line() == symbol.location.line()
|
||||
&& prev.symbolName.contains("~")) {
|
||||
symbol = prev;
|
||||
}
|
||||
}
|
||||
|
||||
if (queryFlags() & QueryMessage::TargetUsrs) {
|
||||
const Set<String> usrs = project()->findTargetUsrs(location);
|
||||
for (const String &usr : usrs) {
|
||||
for (const Symbol &s : project()->findByUsr(usr, location.fileId(), Project::All)) {
|
||||
write(s.toString());
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto targets = RTags::sortTargets(project()->findTargets(symbol));
|
||||
|
||||
int rank = -1;
|
||||
Set<Location> seen;
|
||||
auto writeTarget = [&rank, this, &seen](const Symbol &target) {
|
||||
if (seen.insert(target.location)) {
|
||||
write(target.location);
|
||||
rank = RTags::targetRank(target.kind);
|
||||
}
|
||||
};
|
||||
for (const auto &target : targets) {
|
||||
if (target.location.isNull())
|
||||
continue;
|
||||
|
||||
if (symbol.usr == target.usr) {
|
||||
writeTarget(target);
|
||||
if (queryFlags() & QueryMessage::AllTargets)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
if (queryFlags() & QueryMessage::DeclarationOnly ? target.isDefinition() : !target.isDefinition()) {
|
||||
const auto others = RTags::sortTargets(project()->findTargets(target));
|
||||
bool found = false;
|
||||
for (auto other : others) {
|
||||
if (!other.isNull() && other.usr == target.usr) {
|
||||
found = true;
|
||||
writeTarget(other);
|
||||
if (!(queryFlags() & QueryMessage::AllTargets)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
if (queryFlags() & QueryMessage::AllTargets) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
writeTarget(target);
|
||||
if (!(queryFlags() & QueryMessage::AllTargets))
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef FollowLocationJob_h
|
||||
#define FollowLocationJob_h
|
||||
|
||||
#include "Location.h"
|
||||
#include "QueryJob.h"
|
||||
|
||||
class FollowLocationJob : public QueryJob
|
||||
{
|
||||
public:
|
||||
FollowLocationJob(Location loc, const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Project> &project);
|
||||
protected:
|
||||
virtual int execute() override;
|
||||
private:
|
||||
const Location location;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,151 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "IncludeFileJob.h"
|
||||
|
||||
#include "Project.h"
|
||||
#include "RTags.h"
|
||||
#include "Server.h"
|
||||
|
||||
IncludeFileJob::IncludeFileJob(const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Project> &project)
|
||||
: QueryJob(query, project)
|
||||
{
|
||||
const uint32_t fileId = Location::fileId(query->currentFile());
|
||||
mSource = project->sources(fileId).value(query->buildIndex());
|
||||
if (mSource.isNull()) {
|
||||
for (const uint32_t dep : project->dependencies(fileId, Project::DependsOnArg)) {
|
||||
mSource = project->sources(dep).value(query->buildIndex());
|
||||
if (!mSource.isNull())
|
||||
break;
|
||||
}
|
||||
}
|
||||
mSymbol = query->query();
|
||||
}
|
||||
|
||||
static inline List<Path> headersForSymbol(const std::shared_ptr<Project> &project, Location loc)
|
||||
{
|
||||
List<Path> ret;
|
||||
const Path &path = loc.path();
|
||||
if (path.isHeader()) {
|
||||
ret.append(path);
|
||||
if (const DependencyNode *node = project->dependencies().value(loc.fileId())) {
|
||||
for (const auto &dependent : node->dependents) {
|
||||
const Path p = Location::path(dependent.first);
|
||||
if (p.isHeader() && dependent.second->includes.size() == 1) {
|
||||
ret.append(p);
|
||||
// allow headers that only include one header if we don't
|
||||
// find anything for the real header
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int IncludeFileJob::execute()
|
||||
{
|
||||
if (mSource.isNull()) {
|
||||
return 1;
|
||||
}
|
||||
const Path directory = mSource.sourceFile().parentDir();
|
||||
Set<Location> last;
|
||||
int matches = 0;
|
||||
Set<String> all;
|
||||
auto process = [&directory, this, &all](const Set<Location> &locations) {
|
||||
for (Location loc : locations) {
|
||||
bool first = true;
|
||||
for (const Path &path : headersForSymbol(project(), loc)) {
|
||||
bool found = false;
|
||||
const Symbol sym = project()->findSymbol(loc);
|
||||
switch (sym.kind) {
|
||||
case CXCursor_ClassDecl:
|
||||
case CXCursor_StructDecl:
|
||||
case CXCursor_ClassTemplate:
|
||||
case CXCursor_TypedefDecl:
|
||||
if (!sym.isDefinition())
|
||||
break;
|
||||
RCT_FALL_THROUGH;
|
||||
case CXCursor_FunctionDecl:
|
||||
case CXCursor_FunctionTemplate: {
|
||||
List<String> alternatives;
|
||||
if (path.startsWith(directory))
|
||||
alternatives << String::format<256>("#include \"%s\"", path.mid(directory.size()).constData());
|
||||
for (const Source::Include &inc : mSource.includePaths) {
|
||||
const Path p = inc.path.ensureTrailingSlash();
|
||||
if (path.startsWith(p)) {
|
||||
const String str = String::format<256>("#include <%s>", path.mid(p.size()).constData());
|
||||
alternatives << str;
|
||||
}
|
||||
}
|
||||
const int tail = strlen(path.fileName()) + 1;
|
||||
List<String>::const_iterator it = alternatives.begin();
|
||||
while (it != alternatives.end()) {
|
||||
bool drop = false;
|
||||
for (List<String>::const_iterator it2 = it + 1; it2 != alternatives.end(); ++it2) {
|
||||
if (it2->size() < it->size()) {
|
||||
if (!strncmp(it2->constData() + 9, it->constData() + 9, it2->size() - tail - 9)) {
|
||||
drop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!drop) {
|
||||
found = true;
|
||||
all.insert(*it);
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
break; }
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (first) {
|
||||
if (found)
|
||||
break;
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
project()->findSymbols(mSymbol, [&](Project::SymbolMatchType type, const String &symbolName, const Set<Location> &locations) {
|
||||
++matches;
|
||||
bool fuzzy = false;
|
||||
if (type == Project::StartsWith) {
|
||||
fuzzy = true;
|
||||
const size_t paren = symbolName.indexOf('(');
|
||||
if (paren == mSymbol.size() && !RTags::isFunctionVariable(symbolName))
|
||||
fuzzy = false;
|
||||
}
|
||||
|
||||
if (!fuzzy) {
|
||||
process(locations);
|
||||
} else if (matches == 1) {
|
||||
last = locations;
|
||||
}
|
||||
}, queryFlags());
|
||||
if (matches == 1 && !last.isEmpty()) {
|
||||
process(last);
|
||||
}
|
||||
List<String> alternatives = all.toList();
|
||||
std::sort(alternatives.begin(), alternatives.end(), [](const String &a, const String &b) {
|
||||
return a.size() < b.size();
|
||||
});
|
||||
for (const auto &a : alternatives) {
|
||||
write(a);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef IncludeFileJob_h
|
||||
#define IncludeFileJob_h
|
||||
|
||||
#include "QueryJob.h"
|
||||
#include "rct/String.h"
|
||||
|
||||
class IncludeFileJob : public QueryJob
|
||||
{
|
||||
public:
|
||||
IncludeFileJob(const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Project> &project);
|
||||
protected:
|
||||
virtual int execute() override;
|
||||
private:
|
||||
String mSymbol;
|
||||
Source mSource;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,14 +0,0 @@
|
||||
DECLARE_INCLUDE_TYPE(Type_DirAfter, "-idirafter", " ")
|
||||
DECLARE_INCLUDE_TYPE(Type_File, "-include", " ")
|
||||
DECLARE_INCLUDE_TYPE(Type_Framework, "-F", "")
|
||||
DECLARE_INCLUDE_TYPE(Type_Include, "-I", "")
|
||||
DECLARE_INCLUDE_TYPE(Type_Macros, "-imacros", " ")
|
||||
DECLARE_INCLUDE_TYPE(Type_MultiLib, "-imultilib", " ")
|
||||
DECLARE_INCLUDE_TYPE(Type_PCH, "-include-pch", " ")
|
||||
DECLARE_INCLUDE_TYPE(Type_Prefix, "-iprefix", " ")
|
||||
DECLARE_INCLUDE_TYPE(Type_Quote, "-iquote", " ")
|
||||
DECLARE_INCLUDE_TYPE(Type_Sysroot, "-isysroot", " ")
|
||||
DECLARE_INCLUDE_TYPE(Type_System, "-isystem", " ")
|
||||
DECLARE_INCLUDE_TYPE(Type_SystemFramework, "-iframework", " ")
|
||||
DECLARE_INCLUDE_TYPE(Type_WithPrefix, "-iwithprefix", " ")
|
||||
DECLARE_INCLUDE_TYPE(Type_WithPrefixBefore, "-iwithprefixbefore", " ")
|
||||
@@ -1,129 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef IndexDataMessage_h
|
||||
#define IndexDataMessage_h
|
||||
|
||||
#include "Diagnostic.h"
|
||||
#include "IndexerJob.h"
|
||||
#include "rct/Flags.h"
|
||||
#include "rct/Serializer.h"
|
||||
#include "rct/String.h"
|
||||
#include "RTagsMessage.h"
|
||||
|
||||
class IndexDataMessage : public RTagsMessage
|
||||
{
|
||||
public:
|
||||
enum { MessageId = IndexDataMessageId };
|
||||
|
||||
IndexDataMessage(const std::shared_ptr<IndexerJob> &job)
|
||||
: RTagsMessage(MessageId), mParseTime(0), mId(0), mIndexerJobFlags(job->flags), mBytesWritten(0)
|
||||
{}
|
||||
|
||||
IndexDataMessage()
|
||||
: RTagsMessage(MessageId), mParseTime(0), mId(0), mBytesWritten(0)
|
||||
{}
|
||||
|
||||
void encode(Serializer &serializer) const;
|
||||
void decode(Deserializer &deserializer);
|
||||
|
||||
enum Flag {
|
||||
None = 0x0,
|
||||
ParseFailure = 0x1,
|
||||
InclusionError = 0x2,
|
||||
UsedPCH = 0x4
|
||||
};
|
||||
Flags<Flag> flags() const { return mFlags; }
|
||||
void setFlags(Flags<Flag> f) { mFlags = f; }
|
||||
void setFlag(Flag flag, bool on = true) { mFlags.set(flag, on); }
|
||||
|
||||
Set<uint32_t> visitedFiles() const
|
||||
{
|
||||
Set<uint32_t> ret;
|
||||
for (const auto &it : mFiles) {
|
||||
if (it.second & Visited)
|
||||
ret.insert(it.first);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Set<uint32_t> blockedFiles() const
|
||||
{
|
||||
Set<uint32_t> ret;
|
||||
for (const auto &it : mFiles) {
|
||||
if (!(it.second & Visited))
|
||||
ret.insert(it.first);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
const Path &project() const { return mProject; }
|
||||
void setProject(const Path &p) { mProject = p; }
|
||||
|
||||
uint64_t id() const { return mId; }
|
||||
void setId(uint64_t i) { mId = i; }
|
||||
|
||||
uint64_t parseTime() const { return mParseTime; }
|
||||
void setParseTime(uint64_t time) { mParseTime = time; }
|
||||
|
||||
Flags<IndexerJob::Flag> indexerJobFlags() const { return mIndexerJobFlags; }
|
||||
void setIndexerJobFlags(Flags<IndexerJob::Flag> flags) { mIndexerJobFlags = flags; }
|
||||
|
||||
const String &message() const { return mMessage; }
|
||||
void setMessage(const String &msg) { mMessage = msg; }
|
||||
|
||||
FixIts &fixIts() { return mFixIts; }
|
||||
Diagnostics &diagnostics() { return mDiagnostics; }
|
||||
Includes &includes() { return mIncludes; }
|
||||
enum FileFlag {
|
||||
NoFileFlag = 0x0,
|
||||
Visited = 0x1,
|
||||
HeaderError = 0x2,
|
||||
IncludeError = 0x4
|
||||
};
|
||||
Hash<uint32_t, Flags<FileFlag> > &files() { return mFiles; }
|
||||
const Hash<uint32_t, Flags<FileFlag> > &files() const { return mFiles; }
|
||||
|
||||
size_t bytesWritten() const { return mBytesWritten; }
|
||||
void setBytesWritten(size_t bytes) { mBytesWritten = bytes; }
|
||||
private:
|
||||
Path mProject;
|
||||
uint64_t mParseTime, mId;
|
||||
Flags<IndexerJob::Flag> mIndexerJobFlags; // indexerjobflags
|
||||
String mMessage; // used as output for dump when flags & Dump
|
||||
FixIts mFixIts;
|
||||
Diagnostics mDiagnostics;
|
||||
Includes mIncludes;
|
||||
Hash<uint32_t, Flags<FileFlag> > mFiles;
|
||||
Flags<Flag> mFlags;
|
||||
size_t mBytesWritten;
|
||||
};
|
||||
|
||||
RCT_FLAGS(IndexDataMessage::Flag);
|
||||
RCT_FLAGS(IndexDataMessage::FileFlag);
|
||||
|
||||
inline void IndexDataMessage::encode(Serializer &serializer) const
|
||||
{
|
||||
serializer << mProject << mParseTime << mId << mIndexerJobFlags << mMessage
|
||||
<< mFixIts << mIncludes << mDiagnostics << mFiles << mFlags << mBytesWritten;
|
||||
}
|
||||
|
||||
inline void IndexDataMessage::decode(Deserializer &deserializer)
|
||||
{
|
||||
deserializer >> mProject >> mParseTime >> mId >> mIndexerJobFlags >> mMessage
|
||||
>> mFixIts >> mIncludes >> mDiagnostics >> mFiles >> mFlags >> mBytesWritten;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,35 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "IndexMessage.h"
|
||||
|
||||
#include "rct/Serializer.h"
|
||||
|
||||
IndexMessage::IndexMessage()
|
||||
: RTagsMessage(MessageId), mFlags(None)
|
||||
{
|
||||
}
|
||||
|
||||
void IndexMessage::encode(Serializer &serializer) const
|
||||
{
|
||||
serializer << mCommandLine << mWorkingDirectory << mProjectRoot
|
||||
<< mCompileCommands << mArgs << mFlags << mEnvironment;
|
||||
}
|
||||
|
||||
void IndexMessage::decode(Deserializer &deserializer)
|
||||
{
|
||||
deserializer >> mCommandLine >> mWorkingDirectory >> mProjectRoot
|
||||
>> mCompileCommands >> mArgs >> mFlags >> mEnvironment;
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef IndexMessage_h
|
||||
#define IndexMessage_h
|
||||
|
||||
#include "rct/Flags.h"
|
||||
#include "rct/List.h"
|
||||
#include "rct/String.h"
|
||||
#include "RTagsMessage.h"
|
||||
|
||||
class IndexMessage : public RTagsMessage
|
||||
{
|
||||
public:
|
||||
enum { MessageId = CompileId };
|
||||
|
||||
IndexMessage();
|
||||
|
||||
const Path &projectRoot() const { return mProjectRoot; }
|
||||
void setProjectRoot(const Path &projectRoot) { mProjectRoot = projectRoot; }
|
||||
const Path &workingDirectory() const { return mWorkingDirectory; }
|
||||
void setWorkingDirectory(Path &&wd) { mWorkingDirectory = std::move(wd); }
|
||||
void setEnvironment(const List<String> &environment) { mEnvironment = environment; }
|
||||
const List<String> &environment() const { return mEnvironment; }
|
||||
List<String> &&takeEnvironment() { return std::move(mEnvironment); }
|
||||
Path compileCommands() const { return mCompileCommands; }
|
||||
void setCompileCommands(Path &&path) { mCompileCommands = std::move(path); }
|
||||
const String &arguments() const { return mArgs; }
|
||||
String &&takeArguments() { return std::move(mArgs); }
|
||||
void setArguments(String &&args) { mArgs = std::move(args); }
|
||||
enum Flag {
|
||||
None = 0x0,
|
||||
GuessFlags = 0x1
|
||||
};
|
||||
Flags<Flag> flags() const { return mFlags; }
|
||||
void setFlags(Flags<Flag> f) { mFlags = f; }
|
||||
void setFlag(Flag flag, bool on = true) { mFlags.set(flag, on); }
|
||||
virtual void encode(Serializer &serializer) const override;
|
||||
virtual void decode(Deserializer &deserializer) override;
|
||||
private:
|
||||
Path mWorkingDirectory;
|
||||
String mArgs;
|
||||
List<String> mEnvironment;
|
||||
Path mProjectRoot;
|
||||
Path mCompileCommands;
|
||||
Flags<Flag> mFlags;
|
||||
};
|
||||
|
||||
RCT_FLAGS(IndexMessage::Flag);
|
||||
|
||||
#endif
|
||||
@@ -1,47 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "IndexParseData.h"
|
||||
#include "Location.h"
|
||||
|
||||
bool IndexParseData::write(const std::function<bool(const String &)> &write, const Match &match) const
|
||||
{
|
||||
auto process = [&write, &match](const String &str, const Sources &sss) {
|
||||
if (!sss.isEmpty()) {
|
||||
if (!write(str + ":"))
|
||||
return false;
|
||||
|
||||
for (const auto &ss : sss) {
|
||||
const Path file = Location::path(ss.first);
|
||||
if (match.isEmpty() || match.match(file)) {
|
||||
write(" " + file + ":");
|
||||
for (const auto &s : ss.second) {
|
||||
if (!write(" " + s.toString()))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
if (!process("Sources", sources))
|
||||
return false;
|
||||
|
||||
for (const auto &commands : compileCommands) {
|
||||
if (!process(Location::path(commands.first), commands.second.sources))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef IndexParseData_h
|
||||
#define IndexParseData_h
|
||||
|
||||
#include "rct/Path.h"
|
||||
#include "rct/List.h"
|
||||
#include "rct/Serializer.h"
|
||||
#include "rct/Log.h"
|
||||
#include <cstdint>
|
||||
#include "RTags.h"
|
||||
#include "rct/Connection.h"
|
||||
#include "Match.h"
|
||||
#include <functional>
|
||||
|
||||
class IndexParseData
|
||||
{
|
||||
public:
|
||||
Path project;
|
||||
struct CompileCommands {
|
||||
CompileCommands()
|
||||
: lastModifiedMs(0)
|
||||
{}
|
||||
CompileCommands(CompileCommands &&other)
|
||||
: lastModifiedMs(other.lastModifiedMs), sources(std::move(other.sources)), environment(std::move(other.environment))
|
||||
{
|
||||
other.lastModifiedMs = 0;
|
||||
}
|
||||
CompileCommands(const CompileCommands &other)
|
||||
: lastModifiedMs(other.lastModifiedMs), sources(other.sources), environment(other.environment)
|
||||
{}
|
||||
|
||||
CompileCommands &operator=(CompileCommands &&other)
|
||||
{
|
||||
lastModifiedMs = other.lastModifiedMs;
|
||||
sources = std::move(other.sources);
|
||||
environment = std::move(other.environment);
|
||||
other.lastModifiedMs = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CompileCommands &operator=(const CompileCommands &other)
|
||||
{
|
||||
lastModifiedMs = other.lastModifiedMs;
|
||||
sources = other.sources;
|
||||
environment = other.environment;
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint64_t lastModifiedMs;
|
||||
Sources sources;
|
||||
List<String> environment;
|
||||
};
|
||||
Hash<uint32_t, CompileCommands> compileCommands; // fileId for compile_commands.json -> CompileCommands
|
||||
List<String> environment;
|
||||
Sources sources;
|
||||
|
||||
bool isEmpty() const { return compileCommands.isEmpty() && environment.isEmpty() && sources.isEmpty(); }
|
||||
bool write(const std::function<bool(const String &)> &write, const Match &match = Match()) const;
|
||||
};
|
||||
|
||||
inline Serializer &operator<<(Serializer &s, const IndexParseData::CompileCommands &commands)
|
||||
{
|
||||
s << commands.lastModifiedMs << commands.sources << Sandbox::encoded(commands.environment);
|
||||
return s;
|
||||
}
|
||||
|
||||
inline Deserializer &operator>>(Deserializer &s, IndexParseData::CompileCommands &commands)
|
||||
{
|
||||
s >> commands.lastModifiedMs >> commands.sources >> commands.environment;
|
||||
Sandbox::decode(commands.environment);
|
||||
return s;
|
||||
}
|
||||
|
||||
inline Serializer &operator<<(Serializer &s, const IndexParseData &data)
|
||||
{
|
||||
s << Sandbox::encoded(data.project) << static_cast<uint32_t>(data.compileCommands.size());
|
||||
for (const auto &pair : data.compileCommands) {
|
||||
s << Location::path(pair.first) << pair.first << pair.second;
|
||||
}
|
||||
s << data.sources << Sandbox::encoded(data.environment);
|
||||
return s;
|
||||
}
|
||||
|
||||
inline Deserializer &operator>>(Deserializer &s, IndexParseData &data)
|
||||
{
|
||||
s >> data.project;
|
||||
data.compileCommands.clear();
|
||||
uint32_t size;
|
||||
s >> size;
|
||||
while (size-- > 0) {
|
||||
Path file;
|
||||
s >> file;
|
||||
uint32_t fileId;
|
||||
s >> fileId;
|
||||
Location::set(file, fileId);
|
||||
s >> data.compileCommands[fileId];
|
||||
}
|
||||
s >> data.sources >> data.environment;
|
||||
Sandbox::decode(data.environment);
|
||||
return s;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,215 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "IndexerJob.h"
|
||||
|
||||
#include "CompilerManager.h"
|
||||
#include "Project.h"
|
||||
#include "rct/Process.h"
|
||||
#include "JobScheduler.h"
|
||||
#include "RTags.h"
|
||||
#include "Server.h"
|
||||
#include "RTagsVersion.h"
|
||||
|
||||
uint64_t IndexerJob::sNextId = 1;
|
||||
IndexerJob::IndexerJob(const SourceList &s,
|
||||
Flags<Flag> f,
|
||||
const std::shared_ptr<Project> &p,
|
||||
const UnsavedFiles &u)
|
||||
: id(0), flags(f),
|
||||
project(p->path()), unsavedFiles(u), crashCount(0), mCachedPriority(INT_MIN)
|
||||
{
|
||||
sources.append(s.front());
|
||||
for (size_t i=1; i<s.size(); ++i) {
|
||||
const Source &src = s.at(i);
|
||||
bool found = false;
|
||||
for (size_t j=0; j<sources.size(); ++j) {
|
||||
if (src.compareArguments(sources.at(j))) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
sources.append(src);
|
||||
}
|
||||
|
||||
assert(!sources.isEmpty());
|
||||
sourceFile = s.begin()->sourceFile();
|
||||
acquireId();
|
||||
visited.insert(sources.begin()->fileId);
|
||||
}
|
||||
|
||||
IndexerJob::~IndexerJob()
|
||||
{
|
||||
destroyed(this);
|
||||
}
|
||||
|
||||
void IndexerJob::acquireId()
|
||||
{
|
||||
id = sNextId++;
|
||||
}
|
||||
|
||||
int IndexerJob::priority() const
|
||||
{
|
||||
if (mCachedPriority == INT_MIN) {
|
||||
int ret = 0;
|
||||
Server *server = Server::instance();
|
||||
uint32_t fileId = sources.begin()->fileId;
|
||||
assert(server);
|
||||
if (server->jobScheduler()->hasHeaderError(fileId)) {
|
||||
ret = -1;
|
||||
} else {
|
||||
if (flags & Dirty) {
|
||||
++ret;
|
||||
} else if (flags & Reindex) {
|
||||
ret += 4;
|
||||
}
|
||||
std::shared_ptr<Project> p = server->project(project);
|
||||
if (server->isActiveBuffer(fileId)) {
|
||||
ret += 8;
|
||||
} else if (p) {
|
||||
if (DependencyNode *node = p->dependencyNode(fileId)) {
|
||||
Set<DependencyNode*> seen;
|
||||
seen.insert(node);
|
||||
std::function<bool(const DependencyNode *node)> func = [&seen, server, &func](const DependencyNode *n) {
|
||||
for (const auto &inc : n->includes) {
|
||||
if (seen.insert(inc.second)
|
||||
&& !Location::path(n->fileId).isSystem()
|
||||
&& (server->isActiveBuffer(n->fileId) || func(inc.second))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (func(node))
|
||||
ret += 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (p && server->currentProject() == p)
|
||||
++ret;
|
||||
}
|
||||
mCachedPriority = ret;
|
||||
}
|
||||
return mCachedPriority;
|
||||
}
|
||||
|
||||
String IndexerJob::encode() const
|
||||
{
|
||||
String ret;
|
||||
{
|
||||
Serializer serializer(ret);
|
||||
serializer.write("1234", sizeof(int)); // for size
|
||||
std::shared_ptr<Project> proj = Server::instance()->project(project);
|
||||
const Server::Options &options = Server::instance()->options();
|
||||
serializer << static_cast<uint16_t>(RTags::DatabaseVersion)
|
||||
<< options.sandboxRoot
|
||||
<< id
|
||||
<< options.socketFile
|
||||
<< project
|
||||
<< static_cast<uint32_t>(sources.size());
|
||||
for (Source copy : sources) {
|
||||
if (!(options.options & Server::AllowWErrorAndWFatalErrors)) {
|
||||
int idx = copy.arguments.indexOf("-Werror");
|
||||
if (idx != -1)
|
||||
copy.arguments.removeAt(idx);
|
||||
idx = copy.arguments.indexOf("-Wfatal-errors");
|
||||
if (idx != -1)
|
||||
copy.arguments.removeAt(idx);
|
||||
}
|
||||
copy.arguments << options.defaultArguments;
|
||||
|
||||
if (!(options.options & Server::AllowPedantic)) {
|
||||
const int idx = copy.arguments.indexOf("-Wpedantic");
|
||||
if (idx != -1) {
|
||||
copy.arguments.removeAt(idx);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.options & Server::EnableCompilerManager) {
|
||||
CompilerManager::applyToSource(copy, CompilerManager::IncludeIncludePaths);
|
||||
}
|
||||
|
||||
Server::instance()->filterBlockedArguments(copy);
|
||||
|
||||
for (const auto &inc : options.includePaths) {
|
||||
copy.includePaths << inc;
|
||||
}
|
||||
if (Server::instance()->options().options & Server::PCHEnabled)
|
||||
proj->fixPCH(copy);
|
||||
|
||||
copy.defines << options.defines;
|
||||
if (!(options.options & Server::EnableNDEBUG)) {
|
||||
copy.defines.remove(Source::Define("NDEBUG"));
|
||||
}
|
||||
assert(!sourceFile.isEmpty());
|
||||
copy.encode(serializer, Source::IgnoreSandbox);
|
||||
}
|
||||
assert(proj);
|
||||
Flags<Flag> f = flags;
|
||||
if (Server::instance()->isActiveBuffer(fileId()))
|
||||
f |= Active;
|
||||
serializer << sourceFile
|
||||
<< f
|
||||
<< static_cast<uint32_t>(options.rpVisitFileTimeout)
|
||||
<< static_cast<uint32_t>(options.rpIndexDataMessageTimeout)
|
||||
<< static_cast<uint32_t>(options.rpConnectTimeout)
|
||||
<< static_cast<uint32_t>(options.rpConnectAttempts)
|
||||
<< static_cast<int32_t>(options.rpNiceValue)
|
||||
<< options.options
|
||||
<< unsavedFiles
|
||||
<< options.dataDir
|
||||
<< options.debugLocations;
|
||||
|
||||
proj->encodeVisitedFiles(serializer);
|
||||
}
|
||||
const uint32_t size = ret.size() - sizeof(int);
|
||||
memcpy(&ret[0], &size, sizeof(size));
|
||||
return ret;
|
||||
}
|
||||
|
||||
String IndexerJob::dumpFlags(Flags<Flag> flags)
|
||||
{
|
||||
List<String> ret;
|
||||
if (flags & Dirty) {
|
||||
ret += "Dirty";
|
||||
}
|
||||
if (flags & Reindex) {
|
||||
ret += "Reindex";
|
||||
}
|
||||
if (flags & Compile) {
|
||||
ret += "Compile";
|
||||
}
|
||||
if (flags & Running) {
|
||||
ret += "Running";
|
||||
}
|
||||
if (flags & Crashed) {
|
||||
ret += "Crashed";
|
||||
}
|
||||
if (flags & Aborted) {
|
||||
ret += "Aborted";
|
||||
}
|
||||
if (flags & Complete) {
|
||||
ret += "Complete";
|
||||
}
|
||||
|
||||
return String::join(ret, ", ");
|
||||
}
|
||||
|
||||
void IndexerJob::recalculatePriority()
|
||||
{
|
||||
mCachedPriority = INT_MIN;
|
||||
priority();
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef IndexerJob_h
|
||||
#define IndexerJob_h
|
||||
|
||||
#include "rct/Flags.h"
|
||||
#include "rct/SignalSlot.h"
|
||||
#include "RTags.h"
|
||||
#include "Source.h"
|
||||
|
||||
class IndexerJob
|
||||
{
|
||||
public:
|
||||
enum Flag {
|
||||
None = 0x000,
|
||||
Dirty = 0x001,
|
||||
Reindex = 0x002,
|
||||
Compile = 0x004,
|
||||
Running = 0x010,
|
||||
Crashed = 0x020,
|
||||
Aborted = 0x040,
|
||||
Complete = 0x080,
|
||||
NoAbort = 0x100,
|
||||
Active = 0x200,
|
||||
Type_Mask = Dirty|Compile|Reindex
|
||||
};
|
||||
|
||||
static String dumpFlags(Flags<Flag> flags);
|
||||
|
||||
IndexerJob(const SourceList &sources,
|
||||
Flags<Flag> flags,
|
||||
const std::shared_ptr<Project> &project,
|
||||
const UnsavedFiles &unsavedFiles = UnsavedFiles());
|
||||
~IndexerJob();
|
||||
|
||||
void acquireId();
|
||||
String encode() const;
|
||||
|
||||
uint32_t fileId() const { assert(!sources.isEmpty()); return sources.begin()->fileId; }
|
||||
|
||||
int priority() const;
|
||||
void recalculatePriority();
|
||||
|
||||
uint64_t id;
|
||||
SourceList sources;
|
||||
Path sourceFile;
|
||||
Flags<Flag> flags;
|
||||
Path project;
|
||||
UnsavedFiles unsavedFiles;
|
||||
Set<uint32_t> visited;
|
||||
int crashCount;
|
||||
Signal<std::function<void(IndexerJob *)> > destroyed;
|
||||
|
||||
private:
|
||||
mutable int mCachedPriority;
|
||||
static uint64_t sNextId;
|
||||
};
|
||||
|
||||
RCT_FLAGS(IndexerJob::Flag);
|
||||
|
||||
#endif
|
||||
@@ -1,322 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "JobScheduler.h"
|
||||
|
||||
#include "IndexDataMessage.h"
|
||||
#include "IndexerJob.h"
|
||||
#include "Project.h"
|
||||
#include "rct/Connection.h"
|
||||
#include "rct/Process.h"
|
||||
#include "Server.h"
|
||||
|
||||
enum { MaxPriority = 10 };
|
||||
// we set the priority to be this when a job has been requested and we couldn't load it
|
||||
JobScheduler::JobScheduler()
|
||||
: mProcrastination(0)
|
||||
{}
|
||||
|
||||
JobScheduler::~JobScheduler()
|
||||
{
|
||||
mPendingJobs.deleteAll();
|
||||
if (!mActiveByProcess.isEmpty()) {
|
||||
for (const auto &job : mActiveByProcess) {
|
||||
job.first->kill();
|
||||
delete job.first;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JobScheduler::add(const std::shared_ptr<IndexerJob> &job)
|
||||
{
|
||||
assert(!(job->flags & ~IndexerJob::Type_Mask));
|
||||
std::shared_ptr<Node> node(new Node({ 0, job, 0, 0, 0, String() }));
|
||||
node->job = job;
|
||||
// error() << job->priority << job->sourceFile << mProcrastination;
|
||||
if (mPendingJobs.isEmpty() || job->priority() > mPendingJobs.first()->job->priority()) {
|
||||
mPendingJobs.prepend(node);
|
||||
} else {
|
||||
std::shared_ptr<Node> after = mPendingJobs.last();
|
||||
while (job->priority() > after->job->priority()) {
|
||||
after = after->prev;
|
||||
assert(after);
|
||||
}
|
||||
mPendingJobs.insert(node, after);
|
||||
}
|
||||
assert(!mInactiveById.contains(job->id));
|
||||
mInactiveById[job->id] = node;
|
||||
// error() << "procrash" << mProcrastination << job->sourceFile;
|
||||
if (!mProcrastination)
|
||||
startJobs();
|
||||
}
|
||||
|
||||
uint32_t JobScheduler::hasHeaderError(DependencyNode *node, Set<uint32_t> &seen) const
|
||||
{
|
||||
assert(node);
|
||||
for (auto dep : node->includes) {
|
||||
if (!seen.insert(dep.first))
|
||||
continue;
|
||||
|
||||
if (mHeaderErrors.contains(dep.first)) {
|
||||
return dep.first;
|
||||
}
|
||||
if (uint32_t fileId = hasHeaderError(dep.second, seen)) {
|
||||
return fileId;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t JobScheduler::hasHeaderError(uint32_t file, const std::shared_ptr<Project> &project) const
|
||||
{
|
||||
DependencyNode *node = project->dependencies().value(file);
|
||||
if (!node)
|
||||
return false;
|
||||
Set<uint32_t> seen;
|
||||
return hasHeaderError(node, seen);
|
||||
}
|
||||
|
||||
void JobScheduler::startJobs()
|
||||
{
|
||||
Server *server = Server::instance();
|
||||
assert(server);
|
||||
if (server->suspended()) {
|
||||
warning() << "Suspended, not starting jobs";
|
||||
return;
|
||||
}
|
||||
const auto &options = server->options();
|
||||
std::shared_ptr<Node> jobNode = mPendingJobs.first();
|
||||
auto cont = [&jobNode, this]() {
|
||||
auto tmp = jobNode->next;
|
||||
mPendingJobs.remove(jobNode);
|
||||
jobNode = tmp;
|
||||
};
|
||||
|
||||
while (mActiveByProcess.size() < options.jobCount && jobNode) {
|
||||
assert(jobNode);
|
||||
assert(jobNode->job);
|
||||
assert(!(jobNode->job->flags & (IndexerJob::Running|IndexerJob::Complete|IndexerJob::Crashed|IndexerJob::Aborted)));
|
||||
std::shared_ptr<Project> project = Server::instance()->project(jobNode->job->project);
|
||||
if (!project) {
|
||||
cont();
|
||||
debug() << jobNode->job->sourceFile << "doesn't have a project, discarding";
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint64_t jobId = jobNode->job->id;
|
||||
Process *process = new Process;
|
||||
debug() << "Starting process for" << jobId << jobNode->job->fileId() << jobNode->job.get();
|
||||
List<String> arguments;
|
||||
arguments << "--priority" << String::number(jobNode->job->priority());
|
||||
|
||||
for (int i=logLevel().toInt(); i>0; --i)
|
||||
arguments << "-v";
|
||||
|
||||
process->readyReadStdOut().connect([this](Process *proc) {
|
||||
std::shared_ptr<Node> n = mActiveByProcess[proc];
|
||||
assert(n);
|
||||
n->stdOut.append(proc->readAllStdOut());
|
||||
|
||||
std::regex rx("@CRASH@([^@]*)@CRASH@");
|
||||
std::smatch match;
|
||||
while (std::regex_search(n->stdOut.ref(), match, rx)) {
|
||||
error() << match[1].str();
|
||||
n->stdOut.remove(match.position(), match.length());
|
||||
}
|
||||
});
|
||||
|
||||
if (!process->start(options.rp, arguments)) {
|
||||
error() << "Couldn't start rp" << options.rp << process->errorString();
|
||||
delete process;
|
||||
jobNode->job->flags |= IndexerJob::Crashed;
|
||||
debug() << "job crashed (didn't start)" << jobId << jobNode->job->fileId() << jobNode->job.get();
|
||||
auto msg = std::make_shared<IndexDataMessage>(jobNode->job);
|
||||
msg->setFlag(IndexDataMessage::ParseFailure);
|
||||
jobFinished(jobNode->job, msg);
|
||||
cont();
|
||||
continue;
|
||||
}
|
||||
process->finished().connect([this, jobId](Process *proc) {
|
||||
EventLoop::deleteLater(proc);
|
||||
auto n = mActiveByProcess.take(proc);
|
||||
assert(!n || n->process == proc);
|
||||
const String stdErr = proc->readAllStdErr();
|
||||
if ((n && !n->stdOut.isEmpty()) || !stdErr.isEmpty()) {
|
||||
error() << (n ? ("Output from " + n->job->sourceFile + ":") : String("Orphaned process:"))
|
||||
<< '\n' << stdErr << (n ? n->stdOut : String());
|
||||
}
|
||||
|
||||
if (n) {
|
||||
assert(n->process == proc);
|
||||
n->process = 0;
|
||||
assert(!(n->job->flags & IndexerJob::Aborted));
|
||||
if (!(n->job->flags & IndexerJob::Complete) && proc->returnCode() != 0) {
|
||||
auto nodeById = mActiveById.take(jobId);
|
||||
assert(nodeById);
|
||||
assert(nodeById == n);
|
||||
// job failed, probably no IndexDataMessage coming
|
||||
n->job->flags |= IndexerJob::Crashed;
|
||||
debug() << "job crashed" << jobId << n->job->fileId() << n->job.get();
|
||||
auto msg = std::make_shared<IndexDataMessage>(n->job);
|
||||
msg->setFlag(IndexDataMessage::ParseFailure);
|
||||
jobFinished(n->job, msg);
|
||||
}
|
||||
}
|
||||
startJobs();
|
||||
});
|
||||
|
||||
|
||||
jobNode->process = process;
|
||||
assert(!(jobNode->job->flags & ~IndexerJob::Type_Mask));
|
||||
jobNode->job->flags |= IndexerJob::Running;
|
||||
process->write(jobNode->job->encode());
|
||||
jobNode->started = Rct::monoMs();
|
||||
mActiveByProcess[process] = jobNode;
|
||||
// error() << "STARTING JOB" << node->job->sourceFile;
|
||||
mInactiveById.remove(jobId);
|
||||
mActiveById[jobId] = jobNode;
|
||||
cont();
|
||||
}
|
||||
}
|
||||
|
||||
void JobScheduler::handleIndexDataMessage(const std::shared_ptr<IndexDataMessage> &message)
|
||||
{
|
||||
auto node = mActiveById.take(message->id());
|
||||
if (!node) {
|
||||
debug() << "Got IndexDataMessage for unknown job";
|
||||
return;
|
||||
}
|
||||
debug() << "job got index data message" << node->job->id << node->job->fileId() << node->job.get();
|
||||
jobFinished(node->job, message);
|
||||
}
|
||||
|
||||
void JobScheduler::jobFinished(const std::shared_ptr<IndexerJob> &job, const std::shared_ptr<IndexDataMessage> &message)
|
||||
{
|
||||
bool headerErrorChanged = false;
|
||||
for (const auto &it : message->files()) {
|
||||
if (it.second & IndexDataMessage::HeaderError) {
|
||||
if (mHeaderErrors.insert(it.first))
|
||||
headerErrorChanged = true;
|
||||
} else if (mHeaderErrors.remove(it.first)) {
|
||||
headerErrorChanged = true;
|
||||
}
|
||||
}
|
||||
// mHeaderErrors.unite(message->headerErrors());
|
||||
assert(!(job->flags & IndexerJob::Aborted));
|
||||
assert(job);
|
||||
assert(message);
|
||||
std::shared_ptr<Project> project = Server::instance()->project(job->project);
|
||||
if (!project)
|
||||
return;
|
||||
|
||||
job->flags &= ~IndexerJob::Running;
|
||||
if (!(job->flags & IndexerJob::Crashed)) {
|
||||
job->flags |= IndexerJob::Complete;
|
||||
} else {
|
||||
++job->crashCount;
|
||||
const auto &options = Server::instance()->options();
|
||||
assert(job->crashCount <= options.maxCrashCount);
|
||||
if (job->crashCount < options.maxCrashCount) {
|
||||
project->releaseFileIds(job->visited);
|
||||
EventLoop::eventLoop()->registerTimer([job, this](int) {
|
||||
if (!(job->flags & IndexerJob::Aborted)) {
|
||||
job->flags &= ~IndexerJob::Crashed;
|
||||
job->acquireId();
|
||||
add(job);
|
||||
}
|
||||
}, 500, Timer::SingleShot); // give it 500 ms before we try again
|
||||
return;
|
||||
}
|
||||
debug() << "job crashed too many times" << job->id << job->fileId() << job.get();
|
||||
}
|
||||
project->onJobFinished(job, message);
|
||||
if (headerErrorChanged)
|
||||
sort();
|
||||
}
|
||||
|
||||
void JobScheduler::dump(const std::shared_ptr<Connection> &conn)
|
||||
{
|
||||
if (!mPendingJobs.isEmpty()) {
|
||||
conn->write("Pending:");
|
||||
for (const auto &node : mPendingJobs) {
|
||||
conn->write<128>("%s: %s %d %s",
|
||||
node->job->sourceFile.constData(),
|
||||
node->job->flags.toString().constData(),
|
||||
node->job->priority(),
|
||||
IndexerJob::dumpFlags(node->job->flags).constData());
|
||||
}
|
||||
}
|
||||
if (!mActiveById.isEmpty()) {
|
||||
conn->write("Active:");
|
||||
const unsigned long long now = Rct::monoMs();
|
||||
for (const auto &node : mActiveById) {
|
||||
conn->write<128>("%s: %s priority: %d %s %lldms",
|
||||
node.second->job->sourceFile.constData(),
|
||||
node.second->job->flags.toString().constData(),
|
||||
node.second->job->priority(),
|
||||
IndexerJob::dumpFlags(node.second->job->flags).constData(),
|
||||
now - node.second->started);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JobScheduler::abort(const std::shared_ptr<IndexerJob> &job)
|
||||
{
|
||||
assert(!(job->flags & IndexerJob::Aborted));
|
||||
job->flags |= IndexerJob::Aborted;
|
||||
if (job->flags & IndexerJob::Crashed) {
|
||||
return;
|
||||
}
|
||||
job->flags &= ~IndexerJob::Running;
|
||||
auto node = mActiveById.take(job->id);
|
||||
if (!node) {
|
||||
debug() << "Aborting inactive job" << job->sourceFile << job->fileId() << job->id << job.get();
|
||||
node = mInactiveById.take(job->id);
|
||||
assert(node);
|
||||
mPendingJobs.remove(node);
|
||||
} else {
|
||||
debug() << "Aborting active job" << job->sourceFile << job->fileId() << job->id << job.get();
|
||||
}
|
||||
if (node->process) {
|
||||
debug() << "Killing process" << node->process;
|
||||
node->process->kill();
|
||||
mActiveByProcess.remove(node->process);
|
||||
}
|
||||
}
|
||||
|
||||
void JobScheduler::clearHeaderError(uint32_t file)
|
||||
{
|
||||
if (mHeaderErrors.remove(file))
|
||||
warning() << Location::path(file) << "was touched, starting jobs";
|
||||
}
|
||||
|
||||
void JobScheduler::sort()
|
||||
{
|
||||
std::vector<std::shared_ptr<Node> > nodes(mPendingJobs.size());
|
||||
for (size_t i=0; i<nodes.size(); ++i) {
|
||||
std::shared_ptr<Node> node = mPendingJobs.removeFirst();
|
||||
node->job->recalculatePriority();
|
||||
nodes[i] = std::move(node);
|
||||
}
|
||||
|
||||
std::stable_sort(nodes.begin(), nodes.end(), [](const std::shared_ptr<Node> &l, const std::shared_ptr<Node> &r) -> bool {
|
||||
return l->job->priority() > r->job->priority();
|
||||
});
|
||||
|
||||
for (std::shared_ptr<Node> &n : nodes) {
|
||||
mPendingJobs.append(std::move(n));
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef JobScheduler_h
|
||||
#define JobScheduler_h
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "rct/EmbeddedLinkedList.h"
|
||||
#include "rct/Set.h"
|
||||
#include "rct/Hash.h"
|
||||
#include "rct/String.h"
|
||||
|
||||
class Connection;
|
||||
class IndexDataMessage;
|
||||
class IndexerJob;
|
||||
class Process;
|
||||
class Project;
|
||||
struct DependencyNode;
|
||||
class JobScheduler : public std::enable_shared_from_this<JobScheduler>
|
||||
{
|
||||
public:
|
||||
JobScheduler();
|
||||
~JobScheduler();
|
||||
|
||||
struct JobScope {
|
||||
JobScope(const std::shared_ptr<JobScheduler> &scheduler)
|
||||
: mScheduler(scheduler)
|
||||
{
|
||||
assert(mScheduler);
|
||||
++mScheduler->mProcrastination;
|
||||
}
|
||||
|
||||
~JobScope()
|
||||
{
|
||||
if (!--mScheduler->mProcrastination)
|
||||
mScheduler->startJobs();
|
||||
}
|
||||
|
||||
private:
|
||||
const std::shared_ptr<JobScheduler> mScheduler;
|
||||
};
|
||||
|
||||
void add(const std::shared_ptr<IndexerJob> &job);
|
||||
void handleIndexDataMessage(const std::shared_ptr<IndexDataMessage> &message);
|
||||
void dump(const std::shared_ptr<Connection> &conn);
|
||||
void abort(const std::shared_ptr<IndexerJob> &job);
|
||||
void clearHeaderError(uint32_t file);
|
||||
bool hasHeaderError(uint32_t fileId) const { return mHeaderErrors.contains(fileId); }
|
||||
Set<uint32_t> headerErrors() const { return mHeaderErrors; }
|
||||
void startJobs();
|
||||
size_t pendingJobCount() const { return mPendingJobs.size(); }
|
||||
size_t activeJobCount() const { return mActiveById.size(); }
|
||||
void sort();
|
||||
private:
|
||||
void jobFinished(const std::shared_ptr<IndexerJob> &job, const std::shared_ptr<IndexDataMessage> &message);
|
||||
struct Node {
|
||||
unsigned long long started;
|
||||
std::shared_ptr<IndexerJob> job;
|
||||
Process *process;
|
||||
std::shared_ptr<Node> next, prev;
|
||||
String stdOut;
|
||||
};
|
||||
uint32_t hasHeaderError(DependencyNode *node, Set<uint32_t> &seen) const;
|
||||
uint32_t hasHeaderError(uint32_t file, const std::shared_ptr<Project> &project) const;
|
||||
|
||||
int mProcrastination;
|
||||
Set<uint32_t> mHeaderErrors;
|
||||
EmbeddedLinkedList<std::shared_ptr<Node> > mPendingJobs;
|
||||
Hash<Process *, std::shared_ptr<Node> > mActiveByProcess;
|
||||
Hash<uint64_t, std::shared_ptr<Node> > mActiveById, mInactiveById;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,174 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "ListSymbolsJob.h"
|
||||
|
||||
#include "Project.h"
|
||||
#include "QueryMessage.h"
|
||||
#include "rct/List.h"
|
||||
#include "rct/Log.h"
|
||||
#include "RTags.h"
|
||||
#include "Server.h"
|
||||
|
||||
const Flags<QueryJob::JobFlag> defaultFlags = (QueryJob::WriteUnfiltered | QueryJob::QuietJob);
|
||||
const Flags<QueryJob::JobFlag> elispFlags = (defaultFlags | QueryJob::QuoteOutput);
|
||||
|
||||
ListSymbolsJob::ListSymbolsJob(const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Project> &proj)
|
||||
: QueryJob(query, proj, query->flags() & QueryMessage::Elisp ? elispFlags : defaultFlags),
|
||||
string(query->query())
|
||||
{
|
||||
}
|
||||
|
||||
int ListSymbolsJob::execute()
|
||||
{
|
||||
Set<String> out;
|
||||
std::shared_ptr<Project> proj = project();
|
||||
if (proj) {
|
||||
if (queryFlags() & QueryMessage::WildcardSymbolNames
|
||||
&& (string.contains('*') || string.contains('?')) && !string.endsWith('*')) {
|
||||
string += '*';
|
||||
}
|
||||
List<QueryMessage::PathFilter> filters = pathFilters();
|
||||
List<Path> paths;
|
||||
for (const auto &filter : filters) {
|
||||
if (filter.mode == QueryMessage::PathFilter::Self) {
|
||||
paths.append(filter.pattern);
|
||||
if (!paths.last().isFile()) {
|
||||
paths.clear();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
paths.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!paths.isEmpty()) {
|
||||
out = listSymbolsWithPathFilter(proj, paths);
|
||||
} else {
|
||||
out = listSymbols(proj);
|
||||
}
|
||||
}
|
||||
|
||||
if (queryFlags() & QueryMessage::Elisp) {
|
||||
write("(list", IgnoreMax | DontQuote);
|
||||
for (Set<String>::const_iterator it = out.begin(); it != out.end(); ++it) {
|
||||
write(*it);
|
||||
}
|
||||
write(")", IgnoreMax | DontQuote);
|
||||
} else {
|
||||
List<String> sorted = out.toList();
|
||||
if (queryFlags() & QueryMessage::ReverseSort) {
|
||||
std::sort(sorted.begin(), sorted.end(), std::greater<String>());
|
||||
} else {
|
||||
std::sort(sorted.begin(), sorted.end());
|
||||
}
|
||||
const int count = sorted.size();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
write(sorted.at(i));
|
||||
}
|
||||
}
|
||||
return out.isEmpty() ? 1 : 0;
|
||||
}
|
||||
|
||||
Set<String> ListSymbolsJob::listSymbolsWithPathFilter(const std::shared_ptr<Project> &project, const List<Path> &paths) const
|
||||
{
|
||||
Set<String> out;
|
||||
const bool wildcard = queryFlags() & QueryMessage::WildcardSymbolNames && (string.contains('*') || string.contains('?'));
|
||||
const bool stripParentheses = queryFlags() & QueryMessage::StripParentheses;
|
||||
const bool caseInsensitive = queryFlags() & QueryMessage::MatchCaseInsensitive;
|
||||
const String::CaseSensitivity cs = caseInsensitive ? String::CaseInsensitive : String::CaseSensitive;
|
||||
for (size_t i=0; i<paths.size(); ++i) {
|
||||
const Path file = paths.at(i);
|
||||
const uint32_t fileId = Location::fileId(file);
|
||||
if (!fileId)
|
||||
continue;
|
||||
auto symbols = project->openSymbols(fileId);
|
||||
if (!symbols)
|
||||
continue;
|
||||
const int count = symbols->count();
|
||||
for (int j=0; j<count; ++j) {
|
||||
const Symbol &symbol = symbols->valueAt(j);
|
||||
if (!filterKind(symbol)) {
|
||||
continue;
|
||||
}
|
||||
const String &symbolName = symbol.symbolName;
|
||||
if (symbolName.isEmpty())
|
||||
continue;
|
||||
if (!string.isEmpty()) {
|
||||
if (wildcard) {
|
||||
if (!Project::matchSymbolName(string, symbolName, cs)) {
|
||||
continue;
|
||||
}
|
||||
} else if (!symbolName.contains(string, cs)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (stripParentheses) {
|
||||
const int paren = symbolName.indexOf('(');
|
||||
if (paren == -1) {
|
||||
out.insert(symbolName);
|
||||
} else {
|
||||
if (!RTags::isFunctionVariable(symbolName))
|
||||
out.insert(symbolName.left(paren));
|
||||
}
|
||||
} else {
|
||||
out.insert(symbolName);
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Set<String> ListSymbolsJob::listSymbols(const std::shared_ptr<Project> &project) const
|
||||
{
|
||||
const bool hasFilter = QueryJob::hasFilter();
|
||||
const bool hasKindFilter = QueryJob::hasKindFilter();
|
||||
const bool stripParentheses = queryFlags() & QueryMessage::StripParentheses;
|
||||
|
||||
Set<String> out;
|
||||
auto inserter = [this, &project, hasFilter, hasKindFilter, stripParentheses, &out](Project::SymbolMatchType,
|
||||
const String &str,
|
||||
const Set<Location> &locations) {
|
||||
if (hasFilter) {
|
||||
bool ok = false;
|
||||
for (const auto &l : locations) {
|
||||
if (filter(l.path())) {
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ok)
|
||||
return;
|
||||
}
|
||||
if (hasKindFilter) {
|
||||
const Symbol sym = project->findSymbol(*locations.begin());
|
||||
if (!filterKind(sym))
|
||||
return;
|
||||
}
|
||||
const int paren = str.indexOf('(');
|
||||
if (paren == -1) {
|
||||
out.insert(str);
|
||||
} else {
|
||||
if (!RTags::isFunctionVariable(str))
|
||||
out.insert(str.left(paren));
|
||||
if (!stripParentheses)
|
||||
out.insert(str);
|
||||
}
|
||||
};
|
||||
|
||||
project->findSymbols(string, inserter, queryFlags());
|
||||
return out;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef ListSymbolsJob_h
|
||||
#define ListSymbolsJob_h
|
||||
|
||||
#include "QueryJob.h"
|
||||
#include "rct/String.h"
|
||||
|
||||
template <typename T>
|
||||
class List;
|
||||
class QueryMessage;
|
||||
class ListSymbolsJob : public QueryJob
|
||||
{
|
||||
public:
|
||||
ListSymbolsJob(const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Project> &proj);
|
||||
protected:
|
||||
virtual int execute() override;
|
||||
Set<String> listSymbolsWithPathFilter(const std::shared_ptr<Project> &project, const List<Path> &paths) const;
|
||||
Set<String> listSymbols(const std::shared_ptr<Project> &project) const;
|
||||
private:
|
||||
String string;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,180 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "Location.h"
|
||||
|
||||
#include "rct/Rct.h"
|
||||
#include "RTags.h"
|
||||
#include "Server.h"
|
||||
#include "Project.h"
|
||||
#include "ClangIndexer.h"
|
||||
|
||||
Hash<Path, uint32_t> Location::sPathsToIds;
|
||||
Hash<uint32_t, Path> Location::sIdsToPaths;
|
||||
uint32_t Location::sLastId = 0;
|
||||
std::mutex Location::sMutex;
|
||||
static inline uint64_t createMask(int startBit, int bitCount)
|
||||
{
|
||||
uint64_t mask = 0;
|
||||
for (int i=startBit; i<startBit + bitCount; ++i) {
|
||||
mask |= (static_cast<uint64_t>(1) << i);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
const uint64_t Location::FILEID_MASK = createMask(0, FileBits);
|
||||
const uint64_t Location::LINE_MASK = createMask(FileBits, LineBits);
|
||||
const uint64_t Location::COLUMN_MASK = createMask(FileBits + LineBits, ColumnBits);
|
||||
|
||||
String Location::toString(Flags<ToStringFlag> flags, Hash<Path, String> *contextCache) const
|
||||
{
|
||||
if (isNull())
|
||||
return String();
|
||||
const unsigned int l = line();
|
||||
const unsigned int c = column();
|
||||
int extra = RTags::digits(l) + RTags::digits(c) + 3;
|
||||
String ctx;
|
||||
if (flags & Location::ShowContext) {
|
||||
ctx += '\t';
|
||||
ctx += context(flags, contextCache);
|
||||
extra += ctx.size();
|
||||
}
|
||||
|
||||
Path p = path();
|
||||
if (flags & ConvertToRelative) {
|
||||
Sandbox::encode(p);
|
||||
} else if (!(flags & AbsolutePath) && Server::instance()) {
|
||||
Server *server = Server::instance();
|
||||
if (std::shared_ptr<Project> pp = server->currentProject()) {
|
||||
const Path projectPath = pp->path();
|
||||
if (!projectPath.isEmpty() && p.startsWith(projectPath))
|
||||
p.remove(0, projectPath.size());
|
||||
}
|
||||
}
|
||||
|
||||
String ret(p.size() + extra, ' ');
|
||||
|
||||
const int w = snprintf(ret.data(), ret.size() + extra + 1, "%s:%d:%d:", p.constData(), l, c);
|
||||
if (!ctx.isEmpty()) {
|
||||
memcpy(ret.data() + w, ctx.constData(), ctx.size());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
String Location::context(Flags<ToStringFlag> flags, Hash<Path, String> *cache) const
|
||||
{
|
||||
String copy;
|
||||
String *code = 0;
|
||||
const Path p = path();
|
||||
|
||||
auto readAll = [&p, this]() {
|
||||
if (Server::instance()) {
|
||||
if (auto project = Server::instance()->currentProject()) {
|
||||
const Path f = project->sourceFilePath(fileId(), "unsaved");
|
||||
String contents = f.readAll();
|
||||
if (!contents.isEmpty())
|
||||
return contents;
|
||||
}
|
||||
}
|
||||
return p.readAll();
|
||||
};
|
||||
if (cache) {
|
||||
String &ref = (*cache)[p];
|
||||
if (ref.isEmpty()) {
|
||||
ref = readAll();
|
||||
}
|
||||
code = &ref;
|
||||
} else {
|
||||
copy = readAll();
|
||||
code = ©
|
||||
}
|
||||
|
||||
String ret;
|
||||
if (!code->isEmpty()) {
|
||||
unsigned int l = line();
|
||||
if (!l)
|
||||
return String();
|
||||
const char *ch = code->constData();
|
||||
while (--l) {
|
||||
ch = strchr(ch, '\n');
|
||||
if (!ch)
|
||||
return String();
|
||||
++ch;
|
||||
}
|
||||
const char *end = strchr(ch, '\n');
|
||||
if (!end)
|
||||
return String();
|
||||
|
||||
ret.assign(ch, end - ch);
|
||||
// error() << "foobar" << ret << bool(flags & NoColor);
|
||||
if (!(flags & NoColor)) {
|
||||
const size_t col = column() - 1;
|
||||
if (col + 1 < ret.size()) {
|
||||
size_t last = col;
|
||||
if (ret.at(last) == '~')
|
||||
++last;
|
||||
while (ret.size() > last && (isalnum(ret.at(last)) || ret.at(last) == '_'))
|
||||
++last;
|
||||
static const char *color = "\x1b[32;1m"; // dark yellow
|
||||
static const char *resetColor = "\x1b[0;0m";
|
||||
// error() << "foobar"<< end << col << ret.size();
|
||||
ret.insert(last, resetColor);
|
||||
ret.insert(col, color);
|
||||
}
|
||||
// printf("[%s]\n", ret.constData());
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Location::saveFileIds()
|
||||
{
|
||||
assert(Server::instance());
|
||||
Server::instance()->saveFileIds();
|
||||
}
|
||||
|
||||
bool Location::init(const Hash<Path, uint32_t> &pathsToIds)
|
||||
{
|
||||
LOCK();
|
||||
sPathsToIds = pathsToIds;
|
||||
sIdsToPaths.clear();
|
||||
sLastId = 0;
|
||||
for (const auto &it : sPathsToIds) {
|
||||
assert(!it.first.isEmpty());
|
||||
Path &ref = sIdsToPaths[it.second];
|
||||
if (!ref.isEmpty()) {
|
||||
sPathsToIds.clear();
|
||||
sIdsToPaths.clear();
|
||||
sLastId = 0;
|
||||
return false;
|
||||
}
|
||||
ref = it.first;
|
||||
sLastId = std::max(sLastId, it.second);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Location::init(const Hash<uint32_t, Path> &idsToPaths)
|
||||
{
|
||||
LOCK();
|
||||
sIdsToPaths = idsToPaths;
|
||||
sPathsToIds.clear();
|
||||
sLastId = 0;
|
||||
for (const auto &it : sIdsToPaths) {
|
||||
sPathsToIds[it.second] = it.first;
|
||||
assert(!it.second.isEmpty());
|
||||
sLastId = std::max(sLastId, it.first);
|
||||
}
|
||||
}
|
||||
@@ -1,357 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef Location_h
|
||||
#define Location_h
|
||||
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include <clang-c/Index.h>
|
||||
#include <stdio.h>
|
||||
#if defined(OS_Linux)
|
||||
#include <linux/limits.h>
|
||||
#elif defined(OS_Darwin)
|
||||
#include <sys/syslimits.h>
|
||||
#endif
|
||||
#ifndef RTAGS_SINGLE_THREAD
|
||||
#include <mutex>
|
||||
#define LOCK() const std::lock_guard<std::mutex> lock(sMutex)
|
||||
#else
|
||||
#define LOCK() do {} while (0)
|
||||
#endif
|
||||
|
||||
#include "rct/Flags.h"
|
||||
#include "rct/Log.h"
|
||||
#include "rct/Path.h"
|
||||
#include "rct/Serializer.h"
|
||||
#include "rct/String.h"
|
||||
#include "rct/StackBuffer.h"
|
||||
|
||||
static inline int intCompare(uint32_t l, uint32_t r)
|
||||
{
|
||||
if (l < r)
|
||||
return -1;
|
||||
if (l > r)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
static inline int comparePosition(uint32_t lline, uint32_t lcol, uint32_t rline, uint32_t rcol)
|
||||
{
|
||||
int ret = intCompare(lline, rline);
|
||||
if (!ret)
|
||||
ret = intCompare(lcol, rcol);
|
||||
return ret;
|
||||
}
|
||||
|
||||
class Location
|
||||
{
|
||||
public:
|
||||
uint64_t value;
|
||||
|
||||
Location()
|
||||
: value(0)
|
||||
{}
|
||||
|
||||
Location(uint32_t file, uint32_t l, uint32_t col)
|
||||
: value((static_cast<uint64_t>(col) << (FileBits + LineBits)) | (static_cast<uint64_t>(l) << (FileBits)) | file)
|
||||
{
|
||||
}
|
||||
|
||||
static inline uint32_t fileId(const Path &path)
|
||||
{
|
||||
LOCK();
|
||||
return sPathsToIds.value(path);
|
||||
}
|
||||
static inline Path path(uint32_t id)
|
||||
{
|
||||
LOCK();
|
||||
return sIdsToPaths.value(id);
|
||||
}
|
||||
|
||||
static uint32_t lastId()
|
||||
{
|
||||
LOCK();
|
||||
return sLastId;
|
||||
}
|
||||
|
||||
static uint32_t count()
|
||||
{
|
||||
LOCK();
|
||||
return sIdsToPaths.size();
|
||||
}
|
||||
|
||||
static inline uint32_t insertFile(const Path &path)
|
||||
{
|
||||
bool save = false;
|
||||
(void)save;
|
||||
assert(path.isAbsolute());
|
||||
assert(!path.contains(".."));
|
||||
// in the case of Source::compilerId path can be a symlink
|
||||
uint32_t ret;
|
||||
{
|
||||
LOCK();
|
||||
uint32_t &id = sPathsToIds[path];
|
||||
if (!id) {
|
||||
id = ++sLastId;
|
||||
sIdsToPaths[id] = path;
|
||||
save = true;
|
||||
}
|
||||
ret = id;
|
||||
}
|
||||
#ifndef RTAGS_SINGLE_THREAD
|
||||
if (save)
|
||||
saveFileIds();
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline uint32_t fileId() const { return static_cast<uint32_t>(value & FILEID_MASK); }
|
||||
inline uint32_t line() const { return static_cast<uint32_t>((value & LINE_MASK) >> FileBits); }
|
||||
inline uint32_t column() const { return static_cast<uint32_t>((value & COLUMN_MASK) >> (FileBits + LineBits)); }
|
||||
|
||||
inline Path path() const
|
||||
{
|
||||
LOCK();
|
||||
return sIdsToPaths.value(fileId());
|
||||
}
|
||||
inline bool isNull() const { return !value; }
|
||||
inline bool isValid() const { return value; }
|
||||
inline void clear() { value = 0; }
|
||||
inline bool operator==(Location other) const { return value == other.value; }
|
||||
inline bool operator!=(Location other) const { return value != other.value; }
|
||||
inline int compare(Location other) const
|
||||
{
|
||||
int ret = intCompare(fileId(), other.fileId());
|
||||
if (!ret) {
|
||||
ret = intCompare(line(), other.line());
|
||||
if (!ret)
|
||||
ret = intCompare(column(), other.column());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
inline bool operator<(Location other) const
|
||||
{
|
||||
return compare(other) < 0;
|
||||
}
|
||||
|
||||
inline bool operator<=(Location other) const
|
||||
{
|
||||
return compare(other) <= 0;
|
||||
}
|
||||
|
||||
inline bool operator>(Location other) const
|
||||
{
|
||||
return compare(other) > 0;
|
||||
}
|
||||
|
||||
inline bool operator>=(Location other) const
|
||||
{
|
||||
return compare(other) >= 0;
|
||||
}
|
||||
|
||||
enum ToStringFlag {
|
||||
NoFlag = 0x0,
|
||||
ShowContext = 0x1,
|
||||
NoColor = 0x2,
|
||||
AbsolutePath = 0x4,
|
||||
ConvertToRelative = 0x8
|
||||
};
|
||||
|
||||
String toString(Flags<ToStringFlag> flags = NoFlag, Hash<Path, String> *contextCache = 0) const;
|
||||
String context(Flags<ToStringFlag> flags, Hash<Path, String> *cache = 0) const;
|
||||
|
||||
inline String debug() const;
|
||||
|
||||
enum DecodeFlag {
|
||||
NoDecodeFlag = 0x0,
|
||||
CreateLocation = 0x1
|
||||
};
|
||||
|
||||
static Location decode(const String &data, DecodeFlag flag = NoDecodeFlag)
|
||||
{
|
||||
uint32_t col;
|
||||
uint32_t line;
|
||||
memcpy(&col, data.constData() + data.size() - sizeof(col), sizeof(col));
|
||||
memcpy(&line, data.constData() + data.size() - sizeof(line) - sizeof(col), sizeof(line));
|
||||
Path path(data.constData(), data.size() - sizeof(col) - sizeof(line));
|
||||
uint32_t fileId = Location::fileId(path);
|
||||
if (!fileId) {
|
||||
path.resolve();
|
||||
fileId = Location::fileId(path);
|
||||
}
|
||||
if (!fileId && flag == CreateLocation)
|
||||
fileId = Location::insertFile(path);
|
||||
if (fileId)
|
||||
return Location(fileId, line, col);
|
||||
error("%s:%d:%d is not indexed", path.constData(), line, col);
|
||||
return Location();
|
||||
}
|
||||
static bool parse(const String &str, const Path &pwd, Path::ResolveMode mode,
|
||||
Path *path, uint32_t *line, uint32_t *col)
|
||||
{
|
||||
const size_t lastColon = str.lastIndexOf(':', str.size() - 2);
|
||||
if (lastColon == String::npos)
|
||||
return false;
|
||||
const size_t secondLastColon = str.lastIndexOf(':', lastColon - 1);
|
||||
if (secondLastColon == String::npos)
|
||||
return false;
|
||||
const char *cstr = str.constData();
|
||||
char *end;
|
||||
assert(line);
|
||||
*line = static_cast<uint32_t>(strtoul(cstr + secondLastColon + 1, &end, 10));
|
||||
if (*end != ':' || end == str + secondLastColon + 1)
|
||||
return false;
|
||||
assert(col);
|
||||
*col = static_cast<uint32_t>(strtoul(cstr + lastColon + 1, &end, 10));
|
||||
if ((*end && *end != ':') || end == str + lastColon + 1)
|
||||
return false;
|
||||
|
||||
*path = Path::resolved(str.left(secondLastColon), mode, pwd);
|
||||
return path->isFile();
|
||||
}
|
||||
|
||||
static String encode(const String &str, const Path &pwd = Path())
|
||||
{
|
||||
uint32_t line, col;
|
||||
Path path;
|
||||
if (!parse(str, pwd, Path::MakeAbsolute, &path, &line, &col))
|
||||
return String();
|
||||
char buf[8];
|
||||
memcpy(buf, &line, sizeof(line));
|
||||
memcpy(buf + 4, &col, sizeof(col));
|
||||
path.append(buf, 8);
|
||||
return path;
|
||||
}
|
||||
|
||||
static Location fromPathLineAndColumn(const String &str, const Path &pwd = Path())
|
||||
{
|
||||
uint32_t line, col;
|
||||
Path path;
|
||||
if (!parse(str, pwd, Path::RealPath, &path, &line, &col))
|
||||
return Location();
|
||||
|
||||
const uint32_t fileId = Location::fileId(path);
|
||||
if (!fileId)
|
||||
return Location();
|
||||
return Location(fileId, line, col);
|
||||
}
|
||||
static Hash<uint32_t, Path> idsToPaths()
|
||||
{
|
||||
LOCK();
|
||||
return sIdsToPaths;
|
||||
}
|
||||
static Hash<Path, uint32_t> pathsToIds()
|
||||
{
|
||||
LOCK();
|
||||
return sPathsToIds;
|
||||
}
|
||||
|
||||
static void iterate(std::function<void(const Path &, uint32_t)> func)
|
||||
{
|
||||
LOCK();
|
||||
for (const auto &it : sPathsToIds) {
|
||||
func(it.first, it.second);
|
||||
}
|
||||
}
|
||||
static bool init(const Hash<Path, uint32_t> &pathsToIds);
|
||||
static void init(const Hash<uint32_t, Path> &idsToPaths);
|
||||
|
||||
static void set(const Path &path, uint32_t fileId)
|
||||
{
|
||||
LOCK();
|
||||
uint32_t &refId = sPathsToIds[path];
|
||||
assert(!refId || refId == fileId);
|
||||
refId = fileId;
|
||||
Path &p = sIdsToPaths[fileId];
|
||||
if (p.isEmpty()) {
|
||||
p = path;
|
||||
} else {
|
||||
assert(p == path || path.resolved() == p);
|
||||
}
|
||||
sLastId = std::max(sLastId, fileId);
|
||||
}
|
||||
private:
|
||||
#ifndef RTAGS_SINGLE_THREAD
|
||||
static std::mutex sMutex;
|
||||
static void saveFileIds();
|
||||
#endif
|
||||
static Hash<Path, uint32_t> sPathsToIds;
|
||||
static Hash<uint32_t, Path> sIdsToPaths;
|
||||
static uint32_t sLastId;
|
||||
enum {
|
||||
FileBits = 22,
|
||||
LineBits = 21,
|
||||
ColumnBits = 64 - FileBits - LineBits
|
||||
};
|
||||
static const uint64_t FILEID_MASK;
|
||||
static const uint64_t LINE_MASK;
|
||||
static const uint64_t COLUMN_MASK;
|
||||
};
|
||||
|
||||
RCT_FLAGS(Location::ToStringFlag);
|
||||
|
||||
template <> struct FixedSize<Location>
|
||||
{
|
||||
static constexpr size_t value = sizeof(Location::value);
|
||||
};
|
||||
|
||||
template <> inline Serializer &operator<<(Serializer &s, const Location &t)
|
||||
{
|
||||
s.write(reinterpret_cast<const char*>(&t.value), sizeof(uint64_t));
|
||||
return s;
|
||||
}
|
||||
|
||||
inline bool operator==(Location loc, const String &str)
|
||||
{
|
||||
const Location fromPath = Location::fromPathLineAndColumn(str);
|
||||
return loc == fromPath;
|
||||
}
|
||||
|
||||
inline bool operator!=(Location loc, const String &str)
|
||||
{
|
||||
const Location fromPath = Location::fromPathLineAndColumn(str);
|
||||
return loc != fromPath;
|
||||
}
|
||||
|
||||
inline bool operator==(const String &str, Location loc)
|
||||
{
|
||||
const Location fromPath = Location::fromPathLineAndColumn(str);
|
||||
return loc == fromPath;
|
||||
}
|
||||
|
||||
inline bool operator!=(const String &str, Location loc)
|
||||
{
|
||||
const Location fromPath = Location::fromPathLineAndColumn(str);
|
||||
return loc != fromPath;
|
||||
}
|
||||
|
||||
template <> inline Deserializer &operator>>(Deserializer &s, Location &t)
|
||||
{
|
||||
s.read(reinterpret_cast<char*>(&t), sizeof(uint64_t));
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline Log operator<<(Log dbg, Location loc)
|
||||
{
|
||||
dbg << loc.toString();
|
||||
return dbg;
|
||||
}
|
||||
|
||||
inline String Location::debug() const
|
||||
{
|
||||
return toString(NoColor|AbsolutePath);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,48 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef LogObject_h
|
||||
#define LogObject_h
|
||||
|
||||
#include "rct/Connection.h"
|
||||
#include "rct/Log.h"
|
||||
#include "rct/String.h"
|
||||
|
||||
class LogObject : public LogOutput
|
||||
{
|
||||
public:
|
||||
LogObject(const std::shared_ptr<Connection> &conn, int level)
|
||||
: LogOutput(level), mConnection(conn)
|
||||
{
|
||||
conn->disconnected().connect(std::bind(&LogOutput::remove, this));
|
||||
}
|
||||
|
||||
virtual void log(const char *msg, int len) override
|
||||
{
|
||||
EventLoop::mainEventLoop()->callLaterMove(std::bind((bool(Connection::*)(Message&&))&Connection::send, mConnection, std::placeholders::_1),
|
||||
ResponseMessage(String(msg, len)));
|
||||
}
|
||||
|
||||
virtual bool testLog(int level) const override
|
||||
{
|
||||
if (logLevel() < 0 || level < 0)
|
||||
return level == logLevel();
|
||||
return LogOutput::testLog(level);
|
||||
}
|
||||
private:
|
||||
const std::shared_ptr<Connection> &mConnection;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,48 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef OutputMessage_h
|
||||
#define OutputMessage_h
|
||||
|
||||
#include "RTagsMessage.h"
|
||||
|
||||
class LogOutputMessage : public RTagsMessage
|
||||
{
|
||||
public:
|
||||
enum { MessageId = LogOutputId };
|
||||
|
||||
LogOutputMessage(LogLevel level = LogLevel::Error, unsigned int flags = 0)
|
||||
: RTagsMessage(MessageId), mLevel(level), mFlags(flags)
|
||||
{
|
||||
}
|
||||
|
||||
LogLevel level() const { return mLevel; }
|
||||
unsigned int flags() const { return mFlags; }
|
||||
|
||||
void encode(Serializer &serializer) const
|
||||
{
|
||||
serializer << mCommandLine << mLevel << mFlags;
|
||||
}
|
||||
|
||||
void decode(Deserializer &deserializer)
|
||||
{
|
||||
deserializer >> mCommandLine >> mLevel >> mFlags;
|
||||
}
|
||||
private:
|
||||
LogLevel mLevel;
|
||||
unsigned int mFlags;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,126 +0,0 @@
|
||||
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef Match_h
|
||||
#define Match_h
|
||||
|
||||
#include <regex>
|
||||
|
||||
#include "Location.h"
|
||||
#include "rct/Flags.h"
|
||||
#include "rct/Log.h"
|
||||
#include "rct/Rct.h"
|
||||
#include "rct/String.h"
|
||||
|
||||
class Match
|
||||
{
|
||||
public:
|
||||
enum Flag {
|
||||
Flag_None = 0x0,
|
||||
Flag_StringMatch = 0x1,
|
||||
Flag_Regex = 0x2,
|
||||
Flag_CaseInsensitive = 0x4
|
||||
};
|
||||
|
||||
inline Match()
|
||||
{}
|
||||
|
||||
inline Match(const String &pattern, Flags<Flag> f = Flag_StringMatch);
|
||||
inline Match(Match &&other) noexcept
|
||||
: mRegex(std::move(other.mRegex)), mPattern(std::move(other.mPattern)), mFlags(other.mFlags)
|
||||
{}
|
||||
inline Match(const Match &other)
|
||||
: mRegex(other.mRegex), mPattern(other.mPattern), mFlags(other.mFlags)
|
||||
{}
|
||||
|
||||
Flags<Flag> flags() const { return mFlags; }
|
||||
|
||||
inline bool match(const String &text) const
|
||||
{
|
||||
if (indexIn(text) != String::npos)
|
||||
return true;
|
||||
if (mFlags & Flag_StringMatch)
|
||||
return mPattern.indexOf(text, 0, (mFlags & Flag_CaseInsensitive
|
||||
? String::CaseInsensitive
|
||||
: String::CaseSensitive)) != String::npos;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline size_t indexIn(const String &text) const
|
||||
{
|
||||
size_t index = String::npos;
|
||||
if (mFlags & Flag_StringMatch)
|
||||
index = text.indexOf(mPattern, 0, mFlags & Flag_CaseInsensitive ? String::CaseInsensitive : String::CaseSensitive);
|
||||
if (index == String::npos && mFlags & Flag_Regex) {
|
||||
index = Rct::indexIn(text, mRegex);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
inline bool isEmpty() const
|
||||
{
|
||||
return !mFlags || mPattern.isEmpty();
|
||||
}
|
||||
|
||||
inline std::regex regex() const
|
||||
{
|
||||
return mRegex;
|
||||
}
|
||||
|
||||
inline String pattern() const
|
||||
{
|
||||
return mPattern;
|
||||
}
|
||||
uint32_t fileId() const
|
||||
{
|
||||
return Location::fileId(mPattern);
|
||||
}
|
||||
private:
|
||||
std::regex mRegex;
|
||||
String mPattern;
|
||||
Flags<Flag> mFlags;
|
||||
};
|
||||
RCT_FLAGS(Match::Flag);
|
||||
|
||||
inline Log operator<<(Log log, const Match &match)
|
||||
{
|
||||
String ret = "Match(flags: ";
|
||||
ret += String::number(match.flags().cast<unsigned int>(), 16);
|
||||
if (match.flags() & Match::Flag_Regex)
|
||||
ret += " rx";
|
||||
if (!match.pattern().isEmpty())
|
||||
ret += " pattern: " + match.pattern();
|
||||
ret += ")";
|
||||
log << ret;
|
||||
return log;
|
||||
}
|
||||
|
||||
inline Match::Match(const String &pattern, Flags<Flag> f)
|
||||
: mFlags(f)
|
||||
{
|
||||
if (mFlags & Flag_Regex) {
|
||||
try {
|
||||
mRegex = pattern.ref();
|
||||
} catch (const std::regex_error &err) {
|
||||
mFlags &= ~Flag_Regex;
|
||||
}
|
||||
}
|
||||
mPattern = pattern;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,58 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "Preprocessor.h"
|
||||
#include "Server.h"
|
||||
|
||||
#include "rct/Connection.h"
|
||||
#include "rct/Log.h"
|
||||
#include "rct/Process.h"
|
||||
#include "RTags.h"
|
||||
|
||||
const Flags<Source::CommandLineFlag> SourceFlags = (Source::IncludeSourceFile
|
||||
| Source::IncludeExtraCompiler
|
||||
| Source::ExcludeDefaultArguments
|
||||
| Source::ExcludeDefaultIncludePaths
|
||||
| Source::ExcludeDefaultDefines
|
||||
| Source::IncludeIncludePaths
|
||||
| Source::IncludeDefines);
|
||||
|
||||
Preprocessor::Preprocessor(const Source &source, const std::shared_ptr<Connection> &connection)
|
||||
: mSource(source), mConnection(connection)
|
||||
{
|
||||
Server::instance()->filterBlockedArguments(mSource);
|
||||
mArgs = mSource.toCommandLine(SourceFlags);
|
||||
mArgs.append("-E");
|
||||
mProcess.reset(new Process);
|
||||
mProcess->finished().connect(std::bind(&Preprocessor::onProcessFinished, this));
|
||||
}
|
||||
|
||||
void Preprocessor::preprocess()
|
||||
{
|
||||
mProcess->start(mSource.compiler(), mArgs);
|
||||
}
|
||||
|
||||
void Preprocessor::onProcessFinished()
|
||||
{
|
||||
mConnection->client()->setWriteMode(SocketClient::Synchronous);
|
||||
mConnection->write<256>("// %s %s", mSource.compiler().constData(), String::join(mArgs, ' ').constData());
|
||||
mConnection->write(mProcess->readAllStdOut());
|
||||
const String err = mProcess->readAllStdErr();
|
||||
if (!err.isEmpty()) {
|
||||
mConnection->write<1024>("/* %s */", err.constData());
|
||||
}
|
||||
mConnection->finish();
|
||||
EventLoop::deleteLater(this);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef PREPROCESSOR_H
|
||||
#define PREPROCESSOR_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "Source.h"
|
||||
|
||||
class Connection;
|
||||
class Process;
|
||||
class Preprocessor
|
||||
{
|
||||
public:
|
||||
Preprocessor(const Source &source, const std::shared_ptr<Connection> &connection);
|
||||
|
||||
void preprocess();
|
||||
private:
|
||||
void onProcessFinished();
|
||||
Source mSource;
|
||||
List<String> mArgs;
|
||||
std::shared_ptr<Connection> mConnection;
|
||||
std::unique_ptr<Process> mProcess;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,237 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "ProcThread.h"
|
||||
#include <rct/StopWatch.h>
|
||||
#include "RTags.h"
|
||||
#ifdef RTAGS_HAS_PROC
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
ProcThread::ProcThread(int interval)
|
||||
: Thread(), mInterval(interval)
|
||||
{
|
||||
#ifdef RTAGS_HAS_PROC
|
||||
mPath = "/proc/";
|
||||
#endif
|
||||
}
|
||||
|
||||
ProcThread::~ProcThread()
|
||||
{
|
||||
}
|
||||
|
||||
void ProcThread::run()
|
||||
{
|
||||
while (true) {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
if (!mInterval) {
|
||||
break;
|
||||
}
|
||||
if (mCond.wait_for(lock, std::chrono::milliseconds(mInterval)) != std::cv_status::timeout) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
readProc();
|
||||
}
|
||||
}
|
||||
|
||||
void ProcThread::stop()
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
mInterval = 0;
|
||||
mCond.notify_one();
|
||||
}
|
||||
join();
|
||||
}
|
||||
|
||||
struct ParseNode {
|
||||
char cmdLine[16384];
|
||||
char cwd[PATH_MAX];
|
||||
List<String> environ;
|
||||
|
||||
};
|
||||
void ProcThread::readProc()
|
||||
{
|
||||
for (auto &pair : mNodes) {
|
||||
pair.second = true;
|
||||
}
|
||||
#ifdef RTAGS_HAS_PROC
|
||||
DIR *dir = opendir("/proc/");
|
||||
int found = 0;
|
||||
StopWatch sw;
|
||||
List<ParseNode> nodes(1);
|
||||
while (const dirent *p = readdir(dir)) {
|
||||
#if defined(_DIRENT_HAVE_D_TYPE) && defined(_BSD_SOURCE)
|
||||
if (p->d_type != DT_DIR)
|
||||
continue;
|
||||
#endif
|
||||
const int pid = atoi(p->d_name);
|
||||
if (!pid)
|
||||
continue;
|
||||
auto it = mNodes.find(pid);
|
||||
if (it != mNodes.end()) {
|
||||
it->second = false;
|
||||
continue;
|
||||
} else {
|
||||
mNodes[pid] = false;
|
||||
}
|
||||
|
||||
char file[PATH_MAX]; // should be enough for everyone
|
||||
snprintf(file, sizeof(file), "/proc/%s/cmdline", p->d_name);
|
||||
FILE *f = fopen(file, "r");
|
||||
if (!f) {
|
||||
// error("Can't open %s for reading", file);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto &node = nodes.back();
|
||||
|
||||
size_t read = fread(node.cmdLine, 1, sizeof(node.cmdLine), f);
|
||||
fclose(f);
|
||||
if (!read) {
|
||||
// error("Can't read from %s", file);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strncmp(node.cmdLine, "/bin/bash", 9) || !strncmp(node.cmdLine, "/bin/sh", 7))
|
||||
continue;
|
||||
|
||||
while (read > 0 && !node.cmdLine[read-1])
|
||||
--read; // skip trailing zeroes
|
||||
const char *prev = node.cmdLine;
|
||||
enum {
|
||||
Dunno,
|
||||
Yay,
|
||||
Nay
|
||||
} hasSource = Dunno;
|
||||
for (size_t i=0; i<read; ++i) {
|
||||
switch (node.cmdLine[i]) {
|
||||
case '\n':
|
||||
case '\0':
|
||||
case ' ':
|
||||
if (prev == node.cmdLine) {
|
||||
node.cmdLine[i] = '\0';
|
||||
// error() << "Considering" << node.cmdLine;
|
||||
if (strstr(node.cmdLine, "cc1plus") || strstr(node.cmdLine, "/rc")) {
|
||||
hasSource = Nay;
|
||||
}
|
||||
}
|
||||
node.cmdLine[i] = ' ';
|
||||
if (i && hasSource == Dunno) {
|
||||
for (const char *ext = &node.cmdLine[i - 1]; ext > prev; --ext) {
|
||||
if (*ext == '.') {
|
||||
char extBuf[16];
|
||||
const size_t size = i - 1 - (ext - node.cmdLine);
|
||||
if (size < sizeof(extBuf)) {
|
||||
memcpy(extBuf, ext + 1, size);
|
||||
extBuf[size] = '\0';
|
||||
if (Path::isSource(extBuf)) {
|
||||
hasSource = Yay;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
prev = &node.cmdLine[i];
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasSource != Yay)
|
||||
continue;
|
||||
|
||||
// error() << "GOT SOURCE" << node.cmdLine;
|
||||
|
||||
snprintf(file, sizeof(file), "/proc/%s/cwd", p->d_name);
|
||||
const int w = readlink(file, node.cwd, sizeof(node.cwd) - 2);
|
||||
if (w <= 0) {
|
||||
// error("Can't follow link from %s", file);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node.cwd[w - 1] != '/') {
|
||||
node.cwd[w] = '/';
|
||||
node.cwd[w + 1] = '\0';
|
||||
} else {
|
||||
node.cwd[w] = '\0';
|
||||
}
|
||||
|
||||
snprintf(file, sizeof(file), "/proc/%s/environ", p->d_name);
|
||||
f = fopen(file, "r");
|
||||
if (!f) {
|
||||
// error("Can't open file %s", file);
|
||||
continue;
|
||||
}
|
||||
|
||||
char env[16384];
|
||||
read = fread(env, 1, sizeof(env), f);
|
||||
fclose(f);
|
||||
if (!read) {
|
||||
// error("Can't read from from %s", file);
|
||||
continue;
|
||||
}
|
||||
|
||||
while (read > 0 && !node.cmdLine[read-1])
|
||||
--read; // skip trailing zeroes
|
||||
size_t last = 0;
|
||||
for (size_t i=0; i<read; ++i) {
|
||||
switch (env[i]) {
|
||||
case '\n':
|
||||
case '\0':
|
||||
node.environ.append(String(env + last, i - last));
|
||||
last = i + 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (read > last) {
|
||||
node.environ.append(String(env + last, read - last));
|
||||
}
|
||||
nodes.append(ParseNode());
|
||||
// error() << "GONNA PARSE" << node.cmdLine;
|
||||
// if (!sources.isEmpty()) {
|
||||
// error() << "GOT SOURCES" << sources[0].sourceFile();
|
||||
// }
|
||||
++found;
|
||||
}
|
||||
SourceCache cache;
|
||||
for (size_t i=0; i<nodes.size() - 1; ++i) {
|
||||
const auto &node = nodes.at(i);
|
||||
List<Path> paths;
|
||||
SourceList sources = Source::parse(node.cmdLine, node.cwd, node.environ, &paths, &cache);
|
||||
if (sources.size()) {
|
||||
error() << "GOT SOURCES" << node.cmdLine;
|
||||
}
|
||||
// error() << "GOT SOURCES" << sources.size() << "from" << node.cmdLine
|
||||
// << paths;
|
||||
}
|
||||
|
||||
Hash<int, bool>::iterator it = mNodes.begin();
|
||||
while (it != mNodes.end()) {
|
||||
if (it->second) {
|
||||
mNodes.erase(it++);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
// printf("%d took %llu\n", found, sw.elapsed());
|
||||
closedir(dir);
|
||||
#endif
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef ProcThread_h
|
||||
#define ProcThread_h
|
||||
|
||||
#include <rct/Hash.h>
|
||||
#include <rct/Path.h>
|
||||
#include <rct/Thread.h>
|
||||
#include <rct/SignalSlot.h>
|
||||
#include <rct/String.h>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <time.h>
|
||||
#include "Source.h"
|
||||
|
||||
#if defined(OS_Linux) || defined(OS_FreeBSD) || 1
|
||||
#define RTAGS_HAS_PROC
|
||||
#endif
|
||||
|
||||
class ProcThread : public Thread
|
||||
{
|
||||
public:
|
||||
ProcThread(int interval);
|
||||
~ProcThread();
|
||||
|
||||
void stop();
|
||||
virtual void run();
|
||||
Signal<std::function<void(SourceList)> > &command() { return mCommand; }
|
||||
private:
|
||||
void readProc();
|
||||
|
||||
Signal<std::function<void(SourceList)> > mCommand;
|
||||
std::mutex mMutex;
|
||||
std::condition_variable mCond;
|
||||
int mInterval;
|
||||
|
||||
#ifdef RTAGS_HAS_PROC
|
||||
Path mPath;
|
||||
#endif
|
||||
Hash<int, bool> mNodes;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,502 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef Project_h
|
||||
#define Project_h
|
||||
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
|
||||
#include "Diagnostic.h"
|
||||
#include "FileMap.h"
|
||||
#include "IndexerJob.h"
|
||||
#include "IndexMessage.h"
|
||||
#include "QueryMessage.h"
|
||||
#include "IndexParseData.h"
|
||||
#include "rct/EmbeddedLinkedList.h"
|
||||
#include "rct/FileSystemWatcher.h"
|
||||
#include "rct/Flags.h"
|
||||
#include "rct/Path.h"
|
||||
#include "rct/StopWatch.h"
|
||||
#include "rct/Timer.h"
|
||||
#include "rct/Serializer.h"
|
||||
#include "RTags.h"
|
||||
#include "Token.h"
|
||||
|
||||
class Connection;
|
||||
class Dirty;
|
||||
class FileManager;
|
||||
class IndexDataMessage;
|
||||
class Match;
|
||||
class RestoreThread;
|
||||
struct DependencyNode
|
||||
{
|
||||
enum Flag {
|
||||
Flag_None = 0x0,
|
||||
Flag_IncludeError = 0x1
|
||||
};
|
||||
DependencyNode(uint32_t f, Flags<Flag> l = NullFlags)
|
||||
: fileId(f), flags(l)
|
||||
{}
|
||||
void include(DependencyNode *dependee)
|
||||
{
|
||||
assert(!includes.contains(dependee->fileId) || includes.value(dependee->fileId) == dependee);
|
||||
includes[dependee->fileId] = dependee;
|
||||
assert(!dependee->dependents.contains(fileId) || dependee->dependents.value(fileId) == this);
|
||||
dependee->dependents[fileId] = this;
|
||||
}
|
||||
|
||||
Dependencies dependents, includes;
|
||||
uint32_t fileId;
|
||||
|
||||
Flags<Flag> flags;
|
||||
};
|
||||
|
||||
RCT_FLAGS(DependencyNode::Flag);
|
||||
|
||||
class Project : public std::enable_shared_from_this<Project>
|
||||
{
|
||||
public:
|
||||
Project(const Path &path);
|
||||
~Project();
|
||||
bool init();
|
||||
|
||||
std::shared_ptr<FileManager> fileManager() const { return mFileManager; }
|
||||
|
||||
Path path() const { return mPath; }
|
||||
Path projectDataDir() const { return mProjectDataDir; }
|
||||
bool match(const Match &match, bool *indexed = 0) const;
|
||||
|
||||
enum FileMapType {
|
||||
Symbols,
|
||||
SymbolNames,
|
||||
Targets,
|
||||
Usrs,
|
||||
Tokens
|
||||
};
|
||||
static const char *fileMapName(FileMapType type)
|
||||
{
|
||||
switch (type) {
|
||||
case Symbols: return "symbols";
|
||||
case SymbolNames: return "symnames";
|
||||
case Targets: return "targets";
|
||||
case Usrs: return "usrs";
|
||||
case Tokens: return "tokens";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
std::shared_ptr<FileMap<String, Set<Location> > > openSymbolNames(uint32_t fileId, String *err = 0)
|
||||
{
|
||||
assert(mFileMapScope);
|
||||
return mFileMapScope->openFileMap<String, Set<Location> >(SymbolNames, fileId, mFileMapScope->symbolNames, err);
|
||||
}
|
||||
std::shared_ptr<FileMap<Location, Symbol> > openSymbols(uint32_t fileId, String *err = 0)
|
||||
{
|
||||
assert(mFileMapScope);
|
||||
return mFileMapScope->openFileMap<Location, Symbol>(Symbols, fileId, mFileMapScope->symbols, err);
|
||||
}
|
||||
std::shared_ptr<FileMap<String, Set<Location> > > openTargets(uint32_t fileId, String *err = 0)
|
||||
{
|
||||
assert(mFileMapScope);
|
||||
return mFileMapScope->openFileMap<String, Set<Location> >(Targets, fileId, mFileMapScope->targets, err);
|
||||
}
|
||||
std::shared_ptr<FileMap<String, Set<Location> > > openUsrs(uint32_t fileId, String *err = 0)
|
||||
{
|
||||
assert(mFileMapScope);
|
||||
return mFileMapScope->openFileMap<String, Set<Location> >(Usrs, fileId, mFileMapScope->usrs, err);
|
||||
}
|
||||
|
||||
std::shared_ptr<FileMap<uint32_t, Token> > openTokens(uint32_t fileId, String *err = 0)
|
||||
{
|
||||
assert(mFileMapScope);
|
||||
return mFileMapScope->openFileMap<uint32_t, Token>(Tokens, fileId, mFileMapScope->tokens, err);
|
||||
}
|
||||
|
||||
|
||||
enum DependencyMode {
|
||||
DependsOnArg,
|
||||
ArgDependsOn,
|
||||
All
|
||||
};
|
||||
|
||||
Set<uint32_t> dependencies(uint32_t fileId, DependencyMode mode) const;
|
||||
bool dependsOn(uint32_t source, uint32_t header) const;
|
||||
String dumpDependencies(uint32_t fileId,
|
||||
const List<String> &args = List<String>(),
|
||||
Flags<QueryMessage::Flag> flags = Flags<QueryMessage::Flag>()) const;
|
||||
const Hash<uint32_t, DependencyNode*> &dependencies() const { return mDependencies; }
|
||||
DependencyNode *dependencyNode(uint32_t fileId) const { return mDependencies.value(fileId); }
|
||||
|
||||
static bool readSources(const Path &path, IndexParseData &data, String *error);
|
||||
enum SymbolMatchType {
|
||||
Exact,
|
||||
Wildcard,
|
||||
Regexp,
|
||||
StartsWith
|
||||
};
|
||||
void findSymbols(const String &symbolName,
|
||||
const std::function<void(SymbolMatchType, const String &, const Set<Location> &)> &func,
|
||||
Flags<QueryMessage::Flag> queryFlags,
|
||||
uint32_t fileFilter = 0);
|
||||
|
||||
static bool matchSymbolName(const String &pattern, const String &symbolName, String::CaseSensitivity cs)
|
||||
{
|
||||
return Rct::wildCmp(pattern.constData(), symbolName.constData(), cs);
|
||||
}
|
||||
|
||||
Symbol findSymbol(Location location, int *index = 0);
|
||||
Set<Symbol> findTargets(Location location) { return findTargets(findSymbol(location)); }
|
||||
Set<Symbol> findTargets(const Symbol &symbol);
|
||||
Symbol findTarget(Location location) { return RTags::bestTarget(findTargets(location)); }
|
||||
Symbol findTarget(const Symbol &symbol) { return RTags::bestTarget(findTargets(symbol)); }
|
||||
Set<Symbol> findAllReferences(Location location) { return findAllReferences(findSymbol(location)); }
|
||||
Set<Symbol> findAllReferences(const Symbol &symbol);
|
||||
Set<Symbol> findCallers(Location location) { return findCallers(findSymbol(location)); }
|
||||
Set<Symbol> findCallers(const Symbol &symbol);
|
||||
Set<Symbol> findVirtuals(Location location) { return findVirtuals(findSymbol(location)); }
|
||||
Set<Symbol> findVirtuals(const Symbol &symbol);
|
||||
Set<String> findTargetUsrs(const Symbol &symbol);
|
||||
Set<String> findTargetUsrs(Location loc);
|
||||
Set<Symbol> findSubclasses(const Symbol &symbol);
|
||||
|
||||
Set<Symbol> findByUsr(const String &usr, uint32_t fileId, DependencyMode mode);
|
||||
|
||||
Path sourceFilePath(uint32_t fileId, const char *path = "") const;
|
||||
|
||||
List<RTags::SortedSymbol> sort(const Set<Symbol> &symbols,
|
||||
Flags<QueryMessage::Flag> flags = Flags<QueryMessage::Flag>());
|
||||
|
||||
const Files &files() const { return mFiles; }
|
||||
Files &files() { return mFiles; }
|
||||
|
||||
const Set<uint32_t> &suspendedFiles() const;
|
||||
bool toggleSuspendFile(uint32_t file);
|
||||
void setSuspended(uint32_t file, bool suspended);
|
||||
bool isSuspended(uint32_t file) const;
|
||||
void clearSuspendedFiles();
|
||||
|
||||
bool isIndexed(uint32_t fileId) const;
|
||||
|
||||
void processParseData(IndexParseData &&data);
|
||||
const IndexParseData &indexParseData() const { return mIndexParseData; }
|
||||
void index(const std::shared_ptr<IndexerJob> &job);
|
||||
void reindex(uint32_t fileId, Flags<IndexerJob::Flag> flags);
|
||||
SourceList sources(uint32_t fileId) const;
|
||||
Source source(uint32_t fileId, int buildIndex) const;
|
||||
bool hasSource(uint32_t fileId) const;
|
||||
bool isActiveJob(uint32_t sourceFileId) { return !sourceFileId || mActiveJobs.contains(sourceFileId); }
|
||||
inline bool visitFile(uint32_t fileId, const Path &path, uint32_t sourceFileId);
|
||||
inline void releaseFileIds(const Set<uint32_t> &fileIds);
|
||||
String fixIts(uint32_t fileId) const;
|
||||
int reindex(const Match &match,
|
||||
const std::shared_ptr<QueryMessage> &query,
|
||||
const std::shared_ptr<Connection> &wait);
|
||||
int remove(const Match &match);
|
||||
void onJobFinished(const std::shared_ptr<IndexerJob> &job, const std::shared_ptr<IndexDataMessage> &msg);
|
||||
String toCompileCommands() const;
|
||||
enum WatchMode {
|
||||
Watch_FileManager = 0x1,
|
||||
Watch_SourceFile = 0x2,
|
||||
Watch_Dependency = 0x4,
|
||||
Watch_CompileCommands = 0x8
|
||||
};
|
||||
|
||||
void watch(const Path &dir, WatchMode mode);
|
||||
void unwatch(const Path &dir, WatchMode mode);
|
||||
void clearWatch(Flags<WatchMode> mode);
|
||||
Hash<Path, Flags<WatchMode> > watchedPaths() const { return mWatchedPaths; }
|
||||
|
||||
time_t lastIdleTime() const { return mLastIdleTime; }
|
||||
bool isIndexing() const { return !mActiveJobs.isEmpty(); }
|
||||
void onFileAdded(const Path &path);
|
||||
void onFileModified(const Path &path);
|
||||
void onFileRemoved(const Path &path);
|
||||
void dumpFileMaps(const std::shared_ptr<QueryMessage> &msg, const std::shared_ptr<Connection> &conn);
|
||||
void removeSources(const Hash<uint32_t, uint32_t> &sources); // key fileid, value fileid for compile_commands.json
|
||||
void removeSource(uint32_t fileId);
|
||||
Hash<uint32_t, Path> visitedFiles() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
return mVisitedFiles;
|
||||
}
|
||||
void encodeVisitedFiles(Serializer &serializer)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
serializer << mVisitedFiles;
|
||||
}
|
||||
|
||||
class FileMapScopeScope
|
||||
{
|
||||
public:
|
||||
FileMapScopeScope(Project *p)
|
||||
: mProject(p)
|
||||
{
|
||||
if (mProject)
|
||||
mProject->beginScope();
|
||||
}
|
||||
FileMapScopeScope(const std::shared_ptr<Project> &p)
|
||||
: FileMapScopeScope(p.get())
|
||||
{}
|
||||
|
||||
~FileMapScopeScope()
|
||||
{
|
||||
if (mProject)
|
||||
mProject->endScope();
|
||||
}
|
||||
private:
|
||||
Project *mProject;
|
||||
};
|
||||
|
||||
void beginScope();
|
||||
void endScope();
|
||||
void dirty(uint32_t fileId);
|
||||
bool save();
|
||||
void prepare(uint32_t fileId);
|
||||
String estimateMemory() const;
|
||||
String diagnosticsToString(Flags<QueryMessage::Flag> flags, uint32_t fileId);
|
||||
void diagnose(uint32_t fileId);
|
||||
void diagnoseAll();
|
||||
uint32_t fileMapOptions() const;
|
||||
void fixPCH(Source &source);
|
||||
void includeCompletions(Flags<QueryMessage::Flag> flags, const std::shared_ptr<Connection> &conn, Source &&source) const;
|
||||
size_t bytesWritten() const { return mBytesWritten; }
|
||||
void destroy() { mSaveDirty = false; }
|
||||
enum VisitResult {
|
||||
Stop,
|
||||
Continue,
|
||||
Remove // not allowed for const calls
|
||||
};
|
||||
static void forEachSources(const IndexParseData &data, const std::function<VisitResult(const Sources &sources)>& cb);
|
||||
static void forEachSources(IndexParseData &data, const std::function<VisitResult(Sources &sources)>& cb);
|
||||
void forEachSources(std::function<VisitResult(const Sources &sources)> cb) const { forEachSources(mIndexParseData, cb); }
|
||||
void forEachSources(std::function<VisitResult(Sources &sources)> cb) { forEachSources(mIndexParseData, cb); }
|
||||
|
||||
static void forEachSourceList(const IndexParseData &data, std::function<VisitResult(const SourceList &sources)> cb);
|
||||
static void forEachSourceList(IndexParseData &data, std::function<VisitResult(SourceList &sources)> cb);
|
||||
static void forEachSourceList(Sources &sources, const std::function<VisitResult(SourceList &source)>& cb);
|
||||
static void forEachSourceList(const Sources &sources, const std::function<VisitResult(const SourceList &source)>& cb);
|
||||
void forEachSourceList(std::function<VisitResult(const SourceList &sources)> cb) const { forEachSourceList(mIndexParseData, cb); }
|
||||
void forEachSourceList(std::function<VisitResult(SourceList &sources)> cb) { forEachSourceList(mIndexParseData, cb); }
|
||||
|
||||
static void forEachSource(Sources &sources, const std::function<VisitResult(Source &source)>& cb);
|
||||
static void forEachSource(const Sources &sources, const std::function<VisitResult(const Source &source)>& cb);
|
||||
static void forEachSource(IndexParseData &data, std::function<VisitResult(Source &source)> cb);
|
||||
static void forEachSource(const IndexParseData &data, std::function<VisitResult(const Source &source)> cb);
|
||||
void forEachSource(std::function<VisitResult(const Source &source)> cb) const { forEachSource(mIndexParseData, cb); }
|
||||
void forEachSource(std::function<VisitResult(Source &source)> cb) { forEachSource(mIndexParseData, cb); }
|
||||
void validateAll();
|
||||
void updateDiagnostics(uint32_t fileId, const Diagnostics &diagnostics);
|
||||
private:
|
||||
void reloadCompileCommands();
|
||||
void onFileAddedOrModified(const Path &path);
|
||||
void watchFile(uint32_t fileId);
|
||||
enum ValidateMode {
|
||||
StatOnly,
|
||||
Validate
|
||||
};
|
||||
bool validate(uint32_t fileId, ValidateMode mode, String *error = 0) const;
|
||||
void removeDependencies(uint32_t fileId);
|
||||
void updateDependencies(uint32_t fileId, const std::shared_ptr<IndexDataMessage> &msg);
|
||||
void loadFailed(uint32_t fileId);
|
||||
void updateFixIts(const Set<uint32_t> &visited, FixIts &fixIts);
|
||||
int startDirtyJobs(Dirty *dirty,
|
||||
Flags<IndexerJob::Flag> type,
|
||||
const UnsavedFiles &unsavedFiles = UnsavedFiles(),
|
||||
const std::shared_ptr<Connection> &wait = std::shared_ptr<Connection>());
|
||||
void onDirtyTimeout(Timer *);
|
||||
bool isTemplateDiagnostic(const std::pair<Location, Diagnostic> &diagnostic);
|
||||
|
||||
struct FileMapScope {
|
||||
FileMapScope(const std::shared_ptr<Project> &proj, int m)
|
||||
: project(proj), openedFiles(0), totalOpened(0), max(m), loadFailed(false)
|
||||
{}
|
||||
~FileMapScope()
|
||||
{
|
||||
warning() << "Query opened" << totalOpened << "files for project" << project->path();
|
||||
if (loadFailed)
|
||||
project->validateAll();
|
||||
}
|
||||
|
||||
struct LRUKey {
|
||||
FileMapType type;
|
||||
uint32_t fileId;
|
||||
bool operator<(const LRUKey &other) const
|
||||
{
|
||||
return fileId < other.fileId || (fileId == other.fileId && type < other.type);
|
||||
}
|
||||
};
|
||||
struct LRUEntry {
|
||||
LRUEntry(FileMapType t, uint32_t f)
|
||||
: key({ t, f })
|
||||
{}
|
||||
const LRUKey key;
|
||||
|
||||
std::shared_ptr<LRUEntry> next, prev;
|
||||
};
|
||||
|
||||
void poke(FileMapType t, uint32_t f)
|
||||
{
|
||||
const LRUKey key = { t, f };
|
||||
auto ptr = entryMap.value(key);
|
||||
assert(ptr);
|
||||
entryList.remove(ptr);
|
||||
entryList.append(ptr);
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
std::shared_ptr<FileMap<Key, Value> > openFileMap(FileMapType type, uint32_t fileId,
|
||||
Hash<uint32_t, std::shared_ptr<FileMap<Key, Value> > > &cache,
|
||||
String *errPtr)
|
||||
{
|
||||
auto it = cache.find(fileId);
|
||||
if (it != cache.end()) {
|
||||
poke(type, fileId);
|
||||
return it->second;
|
||||
}
|
||||
const Path path = project->sourceFilePath(fileId, Project::fileMapName(type));
|
||||
auto fileMap = std::make_shared<FileMap<Key, Value>>();
|
||||
String err;
|
||||
if (fileMap->load(path, project->fileMapOptions(), &err)) {
|
||||
++totalOpened;
|
||||
cache[fileId] = fileMap;
|
||||
auto entry = std::make_shared<LRUEntry>(type, fileId);
|
||||
entryList.append(entry);
|
||||
entryMap[entry->key] = entry;
|
||||
if (++openedFiles > max) {
|
||||
const std::shared_ptr<LRUEntry> e = entryList.takeFirst();
|
||||
assert(e);
|
||||
entryMap.remove(e->key);
|
||||
switch (e->key.type) {
|
||||
case SymbolNames:
|
||||
assert(symbolNames.contains(e->key.fileId));
|
||||
symbolNames.remove(e->key.fileId);
|
||||
break;
|
||||
case Symbols:
|
||||
assert(symbols.contains(e->key.fileId));
|
||||
symbols.remove(e->key.fileId);
|
||||
break;
|
||||
case Targets:
|
||||
assert(targets.contains(e->key.fileId));
|
||||
targets.remove(e->key.fileId);
|
||||
break;
|
||||
case Usrs:
|
||||
assert(usrs.contains(e->key.fileId));
|
||||
usrs.remove(e->key.fileId);
|
||||
break;
|
||||
case Tokens:
|
||||
assert(tokens.contains(e->key.fileId));
|
||||
tokens.remove(e->key.fileId);
|
||||
break;
|
||||
}
|
||||
--openedFiles;
|
||||
}
|
||||
assert(openedFiles <= max);
|
||||
} else {
|
||||
if (errPtr) {
|
||||
*errPtr = "Failed to open: " + path + " " + Location::path(fileId) + ": " + err;
|
||||
} else {
|
||||
error() << "Failed to open" << path << Location::path(fileId) << err;
|
||||
}
|
||||
loadFailed = true;
|
||||
fileMap.reset();
|
||||
}
|
||||
return fileMap;
|
||||
}
|
||||
|
||||
Hash<uint32_t, std::shared_ptr<FileMap<String, Set<Location> > > > symbolNames;
|
||||
Hash<uint32_t, std::shared_ptr<FileMap<Location, Symbol> > > symbols;
|
||||
Hash<uint32_t, std::shared_ptr<FileMap<String, Set<Location> > > > targets, usrs;
|
||||
Hash<uint32_t, std::shared_ptr<FileMap<uint32_t, Token> > > tokens;
|
||||
std::shared_ptr<Project> project;
|
||||
int openedFiles, totalOpened;
|
||||
const int max;
|
||||
bool loadFailed;
|
||||
|
||||
EmbeddedLinkedList<std::shared_ptr<LRUEntry> > entryList;
|
||||
Map<LRUKey, std::shared_ptr<LRUEntry> > entryMap;
|
||||
};
|
||||
|
||||
std::shared_ptr<FileMapScope> mFileMapScope;
|
||||
|
||||
const Path mPath, mProjectDataDir;
|
||||
Path mProjectFilePath, mSourcesFilePath;
|
||||
|
||||
Files mFiles;
|
||||
|
||||
Hash<uint32_t, Path> mVisitedFiles;
|
||||
int mJobCounter, mJobsStarted;
|
||||
|
||||
time_t mLastIdleTime;
|
||||
|
||||
Diagnostics mDiagnostics;
|
||||
|
||||
Hash<uint32_t, std::shared_ptr<IndexerJob> > mActiveJobs;
|
||||
|
||||
Timer mDirtyTimer, mReloadCompileCommandsTimer;
|
||||
Set<uint32_t> mPendingDirtyFiles;
|
||||
|
||||
StopWatch mTimer;
|
||||
FileSystemWatcher mWatcher;
|
||||
IndexParseData mIndexParseData;
|
||||
Hash<Path, Flags<WatchMode> > mWatchedPaths;
|
||||
std::shared_ptr<FileManager> mFileManager;
|
||||
FixIts mFixIts;
|
||||
|
||||
Hash<uint32_t, DependencyNode*> mDependencies;
|
||||
Set<uint32_t> mSuspendedFiles;
|
||||
|
||||
size_t mBytesWritten;
|
||||
bool mSaveDirty;
|
||||
|
||||
mutable std::mutex mMutex;
|
||||
};
|
||||
|
||||
RCT_FLAGS(Project::WatchMode);
|
||||
|
||||
inline bool Project::visitFile(uint32_t visitFileId, const Path &path, uint32_t id)
|
||||
{
|
||||
assert(id);
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
assert(visitFileId);
|
||||
Path &p = mVisitedFiles[visitFileId];
|
||||
assert(id);
|
||||
assert(mActiveJobs.contains(id));
|
||||
std::shared_ptr<IndexerJob> &job = mActiveJobs[id];
|
||||
assert(job);
|
||||
if (p.isEmpty()) {
|
||||
p = path;
|
||||
job->visited.insert(visitFileId);
|
||||
return true;
|
||||
}
|
||||
return job->visited.contains(visitFileId);
|
||||
}
|
||||
|
||||
inline void Project::releaseFileIds(const Set<uint32_t> &fileIds)
|
||||
{
|
||||
if (!fileIds.isEmpty()) {
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
for (const auto &f : fileIds) {
|
||||
// error() << "Returning files" << Location::path(f);
|
||||
mVisitedFiles.remove(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline Path Project::sourceFilePath(uint32_t fileId, const char *type) const
|
||||
{
|
||||
return String::format<1024>("%s%d/%s", mProjectDataDir.constData(), fileId, type);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,311 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "QueryJob.h"
|
||||
|
||||
#include <regex>
|
||||
|
||||
#include "Project.h"
|
||||
#include "QueryMessage.h"
|
||||
#include "rct/Connection.h"
|
||||
#include "rct/EventLoop.h"
|
||||
#include "RTags.h"
|
||||
#include "Server.h"
|
||||
|
||||
QueryJob::QueryJob(const std::shared_ptr<QueryMessage> &query,
|
||||
const std::shared_ptr<Project> &proj,
|
||||
Flags<JobFlag> jobFlags)
|
||||
: Project::FileMapScopeScope(proj), mAborted(false), mLinesWritten(0), mQueryMessage(query), mJobFlags(jobFlags), mProject(proj), mFileFilter(0)
|
||||
{
|
||||
assert(query);
|
||||
if (query->flags() & QueryMessage::SilentQuery)
|
||||
setJobFlag(QuietJob);
|
||||
const List<QueryMessage::PathFilter> &pathFilters = query->pathFilters();
|
||||
if (!pathFilters.isEmpty()) {
|
||||
if (pathFilters.size() == 1 && pathFilters.first().mode == QueryMessage::PathFilter::Self) {
|
||||
mFileFilter = Location::fileId(pathFilters.first().pattern);
|
||||
}
|
||||
if (!mFileFilter) {
|
||||
for (const QueryMessage::PathFilter &filter : pathFilters) {
|
||||
if (filter.mode == QueryMessage::PathFilter::Dependency) {
|
||||
uint32_t f = Location::fileId(filter.pattern);
|
||||
if (f && mProject)
|
||||
mFilters.append(std::make_shared<DependencyFilter>(f, mProject));
|
||||
} else if (query->flags() & QueryMessage::MatchRegex) {
|
||||
mFilters.append(std::make_shared<RegexFilter>(filter.pattern));
|
||||
} else {
|
||||
mFilters.append(std::make_shared<PathFilter>(filter.pattern));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mKindFilters = query->kindFilters();
|
||||
}
|
||||
|
||||
QueryJob::~QueryJob()
|
||||
{
|
||||
}
|
||||
|
||||
bool QueryJob::write(const String &out, Flags<WriteFlag> flags)
|
||||
{
|
||||
if ((mJobFlags & WriteUnfiltered) || (flags & Unfiltered) || filter(out)) {
|
||||
if ((mJobFlags & QuoteOutput) && !(flags & DontQuote)) {
|
||||
String o((out.size() * 2) + 2, '"');
|
||||
char *ch = o.data() + 1;
|
||||
int l = 2;
|
||||
for (size_t i=0; i<out.size(); ++i) {
|
||||
const char c = out.at(i);
|
||||
switch (c) {
|
||||
case '"':
|
||||
case '\\':
|
||||
*(ch + 1) = c;
|
||||
*ch = '\\';
|
||||
ch += 2;
|
||||
l += 2;
|
||||
break;
|
||||
default:
|
||||
++l;
|
||||
*ch++ = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
o.truncate(l);
|
||||
return writeRaw(o, flags);
|
||||
} else {
|
||||
return writeRaw(out, flags);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QueryJob::writeRaw(const String &out, Flags<WriteFlag> flags)
|
||||
{
|
||||
assert(mConnection);
|
||||
if (!(flags & IgnoreMax) && mQueryMessage) {
|
||||
const int max = mQueryMessage->max();
|
||||
if (max != -1 && mLinesWritten == max) {
|
||||
return false;
|
||||
}
|
||||
assert(mLinesWritten < max || max == -1);
|
||||
++mLinesWritten;
|
||||
}
|
||||
|
||||
if (!(mJobFlags & QuietJob))
|
||||
warning("=> %s", out.constData());
|
||||
|
||||
if (mConnection) {
|
||||
if (!mConnection->write(out)) {
|
||||
abort();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QueryJob::locationToString(Location location,
|
||||
const std::function<void(LocationPiece, const String &)> &cb,
|
||||
Flags<WriteFlag> writeFlags)
|
||||
{
|
||||
if (location.isNull())
|
||||
return false;
|
||||
Flags<Location::ToStringFlag> kf = locationToStringFlags();
|
||||
kf &= ~Location::ShowContext;
|
||||
cb(Piece_Location, location.toString(kf, &mContextCache));
|
||||
if (!(writeFlags & NoContext) && !(queryFlags() & QueryMessage::NoContext))
|
||||
cb(Piece_Context, location.context(kf, &mContextCache));
|
||||
|
||||
const bool containingFunction = queryFlags() & QueryMessage::ContainingFunction;
|
||||
const bool containingFunctionLocation = queryFlags() & QueryMessage::ContainingFunctionLocation;
|
||||
const bool cursorKind = queryFlags() & QueryMessage::CursorKind;
|
||||
const bool displayName = queryFlags() & QueryMessage::DisplayName;
|
||||
if (containingFunction || containingFunctionLocation || cursorKind || displayName || !mKindFilters.isEmpty()) {
|
||||
int idx;
|
||||
Symbol symbol = project()->findSymbol(location, &idx);
|
||||
if (symbol.isNull()) {
|
||||
error() << "Somehow can't find" << location << "in symbols";
|
||||
} else {
|
||||
if (!mKindFilters.filter(symbol))
|
||||
return false;
|
||||
if (displayName)
|
||||
cb(Piece_SymbolName, symbol.displayName());
|
||||
if (cursorKind)
|
||||
cb(Piece_Kind, symbol.kindSpelling());
|
||||
if (containingFunction || containingFunctionLocation) {
|
||||
const uint32_t fileId = location.fileId();
|
||||
const unsigned int line = location.line();
|
||||
const unsigned int column = location.column();
|
||||
auto fileMap = project()->openSymbols(location.fileId());
|
||||
if (fileMap) {
|
||||
while (idx > 0) {
|
||||
symbol = fileMap->valueAt(--idx);
|
||||
if (symbol.location.fileId() != fileId)
|
||||
break;
|
||||
if (symbol.isDefinition()
|
||||
&& RTags::isContainer(symbol.kind)
|
||||
&& comparePosition(line, column, symbol.startLine, symbol.startColumn) >= 0
|
||||
&& comparePosition(line, column, symbol.endLine, symbol.endColumn) <= 0) {
|
||||
if (containingFunction)
|
||||
cb(Piece_ContainingFunctionName, symbol.symbolName);
|
||||
if (containingFunctionLocation)
|
||||
cb(Piece_ContainingFunctionLocation, symbol.location.toString(locationToStringFlags() & ~Location::ShowContext));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QueryJob::write(Location location, Flags<WriteFlag> flags)
|
||||
{
|
||||
if (location.isNull())
|
||||
return false;
|
||||
if (!(flags & Unfiltered)) {
|
||||
if (!filterLocation(location))
|
||||
return false;
|
||||
flags |= Unfiltered;
|
||||
}
|
||||
|
||||
String out;
|
||||
if (!locationToString(location, [&out](LocationPiece piece, const String &string) {
|
||||
switch (piece) {
|
||||
case Piece_Location:
|
||||
break;
|
||||
case Piece_SymbolName:
|
||||
case Piece_Kind:
|
||||
case Piece_Context:
|
||||
out << '\t';
|
||||
break;
|
||||
case Piece_ContainingFunctionName:
|
||||
case Piece_ContainingFunctionLocation:
|
||||
out << "\tfunction: ";
|
||||
break;
|
||||
}
|
||||
out << string;
|
||||
}, flags))
|
||||
return false;
|
||||
return write(out, flags);
|
||||
}
|
||||
|
||||
bool QueryJob::write(const Symbol &symbol, Flags<WriteFlag> writeFlags)
|
||||
{
|
||||
Flags<Symbol::ToStringFlag> toStringFlags;
|
||||
if (queryFlags() & QueryMessage::SymbolInfoIncludeTargets)
|
||||
toStringFlags |= Symbol::IncludeTargets;
|
||||
if (queryFlags() & QueryMessage::SymbolInfoIncludeReferences)
|
||||
toStringFlags |= Symbol::IncludeReferences;
|
||||
if (queryFlags() & QueryMessage::SymbolInfoIncludeParents)
|
||||
toStringFlags |= Symbol::IncludeParents;
|
||||
if (queryFlags() & QueryMessage::SymbolInfoIncludeBaseClasses)
|
||||
toStringFlags |= Symbol::IncludeBaseClasses;
|
||||
if (queryFlags() & (QueryMessage::ContainingFunction|QueryMessage::JSON|QueryMessage::Elisp))
|
||||
toStringFlags |= Symbol::IncludeContainingFunction;
|
||||
if (queryFlags() & (QueryMessage::ContainingFunctionLocation|QueryMessage::JSON|QueryMessage::Elisp))
|
||||
toStringFlags |= Symbol::IncludeContainingFunctionLocation;
|
||||
|
||||
if (symbol.isNull())
|
||||
return false;
|
||||
|
||||
if (!filterLocation(symbol.location))
|
||||
return false;
|
||||
|
||||
if (!mKindFilters.filter(symbol))
|
||||
return false;
|
||||
|
||||
String out;
|
||||
if (queryFlags() & (QueryMessage::Elisp|QueryMessage::JSON)) {
|
||||
Value val = symbol.toValue(project(), toStringFlags, locationToStringFlags() | Location::NoColor, mPieceFilters);
|
||||
if (queryFlags() & QueryMessage::Elisp) {
|
||||
out = RTags::toElisp(val);
|
||||
} else {
|
||||
out = val.toJSON();
|
||||
}
|
||||
} else {
|
||||
out = symbol.toString(project(), toStringFlags, locationToStringFlags(), mPieceFilters);
|
||||
}
|
||||
return write(out, writeFlags|Unfiltered);
|
||||
}
|
||||
|
||||
bool QueryJob::filter(const String &value) const
|
||||
{
|
||||
if (mFilters.isEmpty() && !(queryFlags() & QueryMessage::FilterSystemIncludes))
|
||||
return true;
|
||||
|
||||
const char *val = value.constData();
|
||||
while (*val && isspace(*val))
|
||||
++val;
|
||||
const char *space = strchr(val, ' ');
|
||||
Path path;
|
||||
uint32_t fileId = 0;
|
||||
if (space) {
|
||||
path.assign(val, space - val);
|
||||
} else {
|
||||
path = val;
|
||||
}
|
||||
|
||||
if (!path.isFile())
|
||||
return true; // non-file things go unfiltered
|
||||
|
||||
if (queryFlags() & QueryMessage::FilterSystemIncludes && Path::isSystem(val))
|
||||
return false;
|
||||
fileId = Location::fileId(path);
|
||||
|
||||
if (mFilters.isEmpty())
|
||||
return true;
|
||||
|
||||
for (const std::shared_ptr<Filter> &filter : mFilters) {
|
||||
if (filter->match(fileId, path))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int QueryJob::run(const std::shared_ptr<Connection> &connection)
|
||||
{
|
||||
assert(connection);
|
||||
mConnection = connection;
|
||||
const int ret = execute();
|
||||
mConnection = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool QueryJob::filterLocation(Location loc) const
|
||||
{
|
||||
if (mFileFilter && loc.fileId() != mFileFilter)
|
||||
return false;
|
||||
const int minLine = mQueryMessage ? mQueryMessage->minLine() : -1;
|
||||
if (minLine != -1) {
|
||||
assert(mQueryMessage);
|
||||
assert(mQueryMessage->maxLine() != -1);
|
||||
const int maxLine = mQueryMessage->maxLine();
|
||||
assert(maxLine != -1);
|
||||
const int line = loc.line();
|
||||
if (line < minLine || line > maxLine) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!mFilters.isEmpty()) {
|
||||
for (const std::shared_ptr<Filter> &filter : mFilters) {
|
||||
if (filter->match(loc.fileId(), loc.path()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef QueryJob_h
|
||||
#define QueryJob_h
|
||||
|
||||
#include <regex>
|
||||
#include <mutex>
|
||||
|
||||
#include "Project.h"
|
||||
#include "QueryMessage.h"
|
||||
#include "rct/Flags.h"
|
||||
#include "rct/List.h"
|
||||
#include "rct/SignalSlot.h"
|
||||
#include "rct/String.h"
|
||||
|
||||
class Location;
|
||||
class QueryMessage;
|
||||
class Connection;
|
||||
struct Symbol;
|
||||
class QueryJob : public Project::FileMapScopeScope
|
||||
{
|
||||
public:
|
||||
enum JobFlag {
|
||||
None = 0x0,
|
||||
WriteUnfiltered = 0x1,
|
||||
QuoteOutput = 0x2,
|
||||
QuietJob = 0x4
|
||||
};
|
||||
enum { Priority = 10 };
|
||||
QueryJob(const std::shared_ptr<QueryMessage> &msg,
|
||||
const std::shared_ptr<Project> &proj,
|
||||
Flags<JobFlag> jobFlags = Flags<JobFlag>());
|
||||
virtual ~QueryJob();
|
||||
|
||||
bool hasFilter() const { return mFileFilter || !mFilters.isEmpty(); }
|
||||
bool hasKindFilter() const { return !mKindFilters.isEmpty(); }
|
||||
bool hasPieceFilter() const { return !mPieceFilters.isEmpty(); }
|
||||
List<QueryMessage::PathFilter> pathFilters() const
|
||||
{
|
||||
if (mQueryMessage)
|
||||
return mQueryMessage->pathFilters();
|
||||
return List<QueryMessage::PathFilter>();
|
||||
}
|
||||
Set<String> pieceFilters() const { return mPieceFilters; }
|
||||
void setPieceFilters(Set<String> &&pieceFilters) { mPieceFilters = std::move(pieceFilters); }
|
||||
uint32_t fileFilter() const { return mFileFilter; }
|
||||
enum WriteFlag {
|
||||
NoWriteFlags = 0x00,
|
||||
IgnoreMax = 0x01,
|
||||
DontQuote = 0x02,
|
||||
Unfiltered = 0x04,
|
||||
NoContext = 0x08
|
||||
};
|
||||
bool write(const String &out, Flags<WriteFlag> flags = Flags<WriteFlag>());
|
||||
bool write(const Symbol &symbol, Flags<WriteFlag> writeFlags = Flags<WriteFlag>());
|
||||
bool write(Location location, Flags<WriteFlag> writeFlags = Flags<WriteFlag>());
|
||||
enum LocationPiece {
|
||||
Piece_Location,
|
||||
Piece_Context,
|
||||
Piece_SymbolName,
|
||||
Piece_Kind,
|
||||
Piece_ContainingFunctionName,
|
||||
Piece_ContainingFunctionLocation
|
||||
};
|
||||
bool locationToString(Location location,
|
||||
const std::function<void(LocationPiece, const String &)> &cb,
|
||||
Flags<WriteFlag> writeFlags = Flags<WriteFlag>());
|
||||
|
||||
template <int StaticBufSize>
|
||||
bool write(Flags<WriteFlag> writeFlags, const char *format, ...) RCT_PRINTF_WARNING(3, 4);
|
||||
template <int StaticBufSize>
|
||||
bool write(const char *format, ...) RCT_PRINTF_WARNING(2, 3);
|
||||
Flags<JobFlag> jobFlags() const { return mJobFlags; }
|
||||
void setJobFlags(Flags<JobFlag> flags) { mJobFlags = flags; }
|
||||
void setJobFlag(JobFlag flag, bool on = true) { mJobFlags.set(flag, on); }
|
||||
Flags<QueryMessage::Flag> queryFlags() const { return mQueryMessage ? mQueryMessage->flags() : Flags<QueryMessage::Flag>(); }
|
||||
std::shared_ptr<QueryMessage> queryMessage() const { return mQueryMessage; }
|
||||
Flags<Location::ToStringFlag> locationToStringFlags() const { return QueryMessage::locationToStringFlags(queryFlags()); }
|
||||
bool filter(const String &val) const;
|
||||
Signal<std::function<void(const String &)> > &output() { return mOutput; }
|
||||
std::shared_ptr<Project> project() const { return mProject; }
|
||||
virtual int execute() = 0;
|
||||
int run(const std::shared_ptr<Connection> &connection = 0);
|
||||
bool isAborted() const { std::lock_guard<std::mutex> lock(mMutex); return mAborted; }
|
||||
void abort() { std::lock_guard<std::mutex> lock(mMutex); mAborted = true; }
|
||||
std::mutex &mutex() const { return mMutex; }
|
||||
const std::shared_ptr<Connection> &connection() const { return mConnection; }
|
||||
bool filterLocation(Location loc) const;
|
||||
bool filterKind(const Symbol &symbol) const { return mKindFilters.filter(symbol); }
|
||||
private:
|
||||
class Filter
|
||||
{
|
||||
public:
|
||||
virtual ~Filter() {}
|
||||
virtual bool match(uint32_t fileId, const Path &path) const = 0;
|
||||
};
|
||||
class PathFilter : public Filter
|
||||
{
|
||||
public:
|
||||
PathFilter(const Path &p) : pattern(p) {}
|
||||
virtual bool match(uint32_t, const Path &path) const { return path.startsWith(pattern); }
|
||||
|
||||
const Path pattern;
|
||||
};
|
||||
class RegexFilter : public Filter
|
||||
{
|
||||
public:
|
||||
RegexFilter(const String &str) : regex(str.ref()) {}
|
||||
virtual bool match(uint32_t, const Path &path) const { return std::regex_search(path.constData(), regex); }
|
||||
|
||||
const std::regex regex;
|
||||
};
|
||||
|
||||
class DependencyFilter : public Filter
|
||||
{
|
||||
public:
|
||||
DependencyFilter(uint32_t f, const std::shared_ptr<Project> &p) : fileId(f), project(p) {}
|
||||
virtual bool match(uint32_t f, const Path &) const { return project->dependsOn(fileId, f); }
|
||||
|
||||
const uint32_t fileId;
|
||||
const std::shared_ptr<Project> project;
|
||||
};
|
||||
|
||||
mutable std::mutex mMutex;
|
||||
bool mAborted;
|
||||
int mLinesWritten;
|
||||
bool writeRaw(const String &out, Flags<WriteFlag> flags);
|
||||
std::shared_ptr<QueryMessage> mQueryMessage;
|
||||
Flags<JobFlag> mJobFlags;
|
||||
Signal<std::function<void(const String &)> > mOutput;
|
||||
std::shared_ptr<Project> mProject;
|
||||
uint32_t mFileFilter;
|
||||
List<std::shared_ptr<Filter> > mFilters;
|
||||
QueryMessage::KindFilters mKindFilters;
|
||||
Set<String> mPieceFilters;
|
||||
String mBuffer;
|
||||
std::shared_ptr<Connection> mConnection;
|
||||
Hash<Path, String> mContextCache;
|
||||
};
|
||||
|
||||
RCT_FLAGS(QueryJob::JobFlag);
|
||||
RCT_FLAGS(QueryJob::WriteFlag);
|
||||
|
||||
template <int StaticBufSize>
|
||||
inline bool QueryJob::write(Flags<WriteFlag> flags, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
const String ret = String::format<StaticBufSize>(format, args);
|
||||
va_end(args);
|
||||
return write(ret, flags);
|
||||
}
|
||||
|
||||
template <int StaticBufSize>
|
||||
inline bool QueryJob::write(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
const String ret = String::format<StaticBufSize>(format, args);
|
||||
va_end(args);
|
||||
return write(ret);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,242 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "QueryMessage.h"
|
||||
|
||||
#include "rct/Serializer.h"
|
||||
#include "RTags.h"
|
||||
|
||||
QueryMessage::QueryMessage(Type type)
|
||||
: RTagsMessage(MessageId), mType(type), mMax(-1), mMinLine(-1), mMaxLine(-1), mBuildIndex(0), mTerminalWidth(-1)
|
||||
{
|
||||
}
|
||||
|
||||
void QueryMessage::encode(Serializer &serializer) const
|
||||
{
|
||||
serializer << mCommandLine << mQuery << mCodeCompletePrefix << mType << mFlags << mMax
|
||||
<< mMinLine << mMaxLine << mBuildIndex << mPathFilters << mKindFilters
|
||||
<< mCurrentFile << mUnsavedFiles << mTerminalWidth
|
||||
#ifdef RTAGS_HAS_LUA
|
||||
<< mVisitASTScripts
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
void QueryMessage::decode(Deserializer &deserializer)
|
||||
{
|
||||
deserializer >> mCommandLine >> mQuery >> mCodeCompletePrefix >> mType >> mFlags >> mMax
|
||||
>> mMinLine >> mMaxLine >> mBuildIndex >> mPathFilters >> mKindFilters
|
||||
>> mCurrentFile >> mUnsavedFiles >> mTerminalWidth
|
||||
#ifdef RTAGS_HAS_LUA
|
||||
>> mVisitASTScripts
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
Flags<Location::ToStringFlag> QueryMessage::locationToStringFlags(Flags<Flag> queryFlags)
|
||||
{
|
||||
Flags<Location::ToStringFlag> ret;
|
||||
if (!(queryFlags & NoContext))
|
||||
ret |= Location::ShowContext;
|
||||
if (queryFlags & NoColor)
|
||||
ret |= Location::NoColor;
|
||||
if (queryFlags & AbsolutePath)
|
||||
ret |= Location::AbsolutePath;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Match QueryMessage::match() const
|
||||
{
|
||||
Flags<Match::Flag> flags = Match::Flag_StringMatch;
|
||||
if (mFlags & MatchRegex)
|
||||
flags |= Match::Flag_Regex;
|
||||
|
||||
return Match(mQuery, flags);
|
||||
}
|
||||
|
||||
QueryMessage::Flag QueryMessage::flagFromString(const String &string)
|
||||
{
|
||||
if (string == "no-context") {
|
||||
return NoContext;
|
||||
} else if (string == "filter-system-includes") {
|
||||
return FilterSystemIncludes;
|
||||
} else if (string == "strip-parentheses") {
|
||||
return StripParentheses;
|
||||
} else if (string == "all-references") {
|
||||
return AllReferences;
|
||||
} else if (string == "reverse-sort") {
|
||||
return ReverseSort;
|
||||
} else if (string == "elisp") {
|
||||
return Elisp;
|
||||
} else if (string == "match-regexp") {
|
||||
return MatchRegex;
|
||||
} else if (string == "match-case-insensitive") {
|
||||
return MatchCaseInsensitive;
|
||||
} else if (string == "find-virtuals") {
|
||||
return FindVirtuals;
|
||||
} else if (string == "silent") {
|
||||
return Silent;
|
||||
} else if (string == "absolute-path") {
|
||||
return AbsolutePath;
|
||||
} else if (string == "find-file-prefer-exact") {
|
||||
return FindFilePreferExact;
|
||||
} else if (string == "symbol-info-include-parents") {
|
||||
return SymbolInfoIncludeParents;
|
||||
} else if (string == "symbol-info-include-targets") {
|
||||
return SymbolInfoIncludeTargets;
|
||||
} else if (string == "symbol-info-include-references") {
|
||||
return SymbolInfoIncludeReferences;
|
||||
} else if (string == "symbol-info-include-base-classes") {
|
||||
return SymbolInfoIncludeBaseClasses;
|
||||
} else if (string == "declaration-only") {
|
||||
return DeclarationOnly;
|
||||
} else if (string == "definiton-only") {
|
||||
return DefinitionOnly;
|
||||
} else if (string == "containing-function") {
|
||||
return ContainingFunction;
|
||||
} else if (string == "cursor-kind") {
|
||||
return CursorKind;
|
||||
} else if (string == "display-name") {
|
||||
return DisplayName;
|
||||
} else if (string == "compilation-flags-only") {
|
||||
return CompilationFlagsOnly;
|
||||
} else if (string == "compilation-flags-split-line") {
|
||||
return CompilationFlagsSplitLine;
|
||||
} else if (string == "dump-include-headers") {
|
||||
return DumpIncludeHeaders;
|
||||
} else if (string == "silent-query") {
|
||||
return SilentQuery;
|
||||
} else if (string == "synchronous-completions") {
|
||||
return SynchronousCompletions;
|
||||
} else if (string == "no-sort-references-by-input") {
|
||||
return NoSortReferencesByInput;
|
||||
} else if (string == "has-location") {
|
||||
return HasLocation;
|
||||
} else if (string == "wildcard-symbol-names") {
|
||||
return WildcardSymbolNames;
|
||||
} else if (string == "no-color") {
|
||||
return NoColor;
|
||||
} else if (string == "target-usrs") {
|
||||
return TargetUsrs;
|
||||
}
|
||||
return NoFlag;
|
||||
}
|
||||
|
||||
bool QueryMessage::KindFilters::filter(const Symbol &symbol) const
|
||||
{
|
||||
if (isEmpty())
|
||||
return true;
|
||||
|
||||
String spelling = Symbol::kindSpelling(symbol.kind).toLower();
|
||||
spelling.remove(' ');
|
||||
auto match = [&spelling, &symbol](const Map<String, Flags<DefinitionType> > &map, bool hasWildcardsOrCategories) {
|
||||
auto it = map.find(spelling);
|
||||
auto matchDefinition = [&symbol](Flags<DefinitionType> f) {
|
||||
f &= Definition|NotDefinition;
|
||||
switch (f.cast<int>()) {
|
||||
case Definition:
|
||||
if (symbol.isDefinition())
|
||||
return true;
|
||||
break;
|
||||
case NotDefinition:
|
||||
if (!symbol.isDefinition())
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
assert(f == (Definition|NotDefinition));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (it != map.end() && matchDefinition(it->second)) {
|
||||
return true;
|
||||
}
|
||||
if (hasWildcardsOrCategories) {
|
||||
for (const auto &pair : map) {
|
||||
if (pair.second & Wildcard) {
|
||||
if (matchDefinition(pair.second) && Rct::wildCmp(pair.first.constData(), spelling.constData())) {
|
||||
return true;
|
||||
}
|
||||
} else if (pair.second & Category && matchDefinition(pair.second)) {
|
||||
if (pair.first == "references") {
|
||||
if (symbol.isReference())
|
||||
return true;
|
||||
} else if (pair.first == "statements") {
|
||||
if (clang_isStatement(symbol.kind))
|
||||
return true;
|
||||
} else if (pair.first == "declarations") {
|
||||
if (clang_isDeclaration(symbol.kind))
|
||||
return true;
|
||||
} else if (pair.first == "expressions") {
|
||||
if (clang_isExpression(symbol.kind))
|
||||
return true;
|
||||
} else if (pair.first == "attributes") {
|
||||
if (clang_isAttribute(symbol.kind))
|
||||
return true;
|
||||
} else if (pair.first == "preprocessing") {
|
||||
if (clang_isPreprocessing(symbol.kind))
|
||||
return true;
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (!out.isEmpty() && match(out, flags & (OutHasWildcards|OutHasCategories))) {
|
||||
return false;
|
||||
}
|
||||
if (in.isEmpty() || match(in, flags & (InHasWildcards|InHasCategories))) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void QueryMessage::KindFilters::insert(const String &arg)
|
||||
{
|
||||
for (String a : arg.toLower().split(',', String::SkipEmpty)) {
|
||||
Flags<DefinitionType> f = Definition|NotDefinition;
|
||||
Map<String, Flags<DefinitionType> > *target = ∈
|
||||
const bool hasWildCard = a.contains("?") || a.contains("*");
|
||||
if (hasWildCard)
|
||||
f |= Wildcard;
|
||||
|
||||
if (a.endsWith('+')) {
|
||||
a.chop(1);
|
||||
f &= ~NotDefinition;
|
||||
} else if (a.endsWith('-')) {
|
||||
a.chop(1);
|
||||
f &= ~Definition;
|
||||
}
|
||||
|
||||
const bool o = a.startsWith('-');
|
||||
if (o) {
|
||||
target = &out;
|
||||
if (hasWildCard)
|
||||
flags |= OutHasWildcards;
|
||||
a.remove(0, 1);
|
||||
} else if (hasWildCard) {
|
||||
flags |= InHasWildcards;
|
||||
}
|
||||
if (a == "references" || a == "statements" || a == "declarations"
|
||||
|| a == "expressions" || a == "attributes" || a == "preprocessing") {
|
||||
f |= Category;
|
||||
flags |= (o ? OutHasCategories : InHasCategories);
|
||||
}
|
||||
(*target)[a] |= f;
|
||||
}
|
||||
}
|
||||
@@ -1,292 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef QUERYMESSAGE_H
|
||||
#define QUERYMESSAGE_H
|
||||
|
||||
#include <algorithm>
|
||||
#include "Location.h"
|
||||
#include "Match.h"
|
||||
#include "rct/Flags.h"
|
||||
#include "rct/Log.h"
|
||||
#include "rct/Path.h"
|
||||
#include "rct/Serializer.h"
|
||||
#include "RTags.h"
|
||||
#include "RTagsMessage.h"
|
||||
|
||||
class QueryMessage : public RTagsMessage
|
||||
{
|
||||
public:
|
||||
enum { MessageId = QueryId };
|
||||
enum Type {
|
||||
Invalid,
|
||||
GenerateTest,
|
||||
CheckReindex,
|
||||
ClassHierarchy,
|
||||
ClearProjects,
|
||||
CodeCompleteAt,
|
||||
DebugLocations,
|
||||
DeleteProject,
|
||||
Dependencies,
|
||||
Diagnose,
|
||||
DumpCompileCommands,
|
||||
DumpCompletions,
|
||||
DumpFile,
|
||||
DumpFileMaps,
|
||||
FindFile,
|
||||
FindSymbols,
|
||||
FixIts,
|
||||
FollowLocation,
|
||||
HasFileManager,
|
||||
IncludeFile,
|
||||
IsIndexed,
|
||||
IsIndexing,
|
||||
JobCount,
|
||||
LastIndexed,
|
||||
ListSymbols,
|
||||
PreprocessFile,
|
||||
Project,
|
||||
ReferencesLocation,
|
||||
ReferencesName,
|
||||
Reindex,
|
||||
ReloadFileManager,
|
||||
RemoveFile,
|
||||
SendDiagnostics,
|
||||
SetBuffers,
|
||||
Sources,
|
||||
Status,
|
||||
Suspend,
|
||||
SymbolInfo,
|
||||
Validate,
|
||||
#ifdef RTAGS_HAS_LUA
|
||||
VisitAST,
|
||||
#endif
|
||||
Tokens
|
||||
};
|
||||
|
||||
enum Flag {
|
||||
NoFlag = 0x0,
|
||||
NoContext = (1ull << 0),
|
||||
FilterSystemIncludes = (1ull << 1),
|
||||
StripParentheses = (1ull << 2),
|
||||
AllReferences = (1ull << 3),
|
||||
ReverseSort = (1ull << 4),
|
||||
Elisp = (1ull << 5),
|
||||
MatchRegex = (1ull << 6),
|
||||
MatchCaseInsensitive = (1ull << 7),
|
||||
FindVirtuals = (1ull << 8),
|
||||
Silent = (1ull << 9),
|
||||
AbsolutePath = (1ull << 10),
|
||||
FindFilePreferExact = (1ull << 11),
|
||||
SymbolInfoIncludeParents = (1ull << 12),
|
||||
SymbolInfoIncludeTargets = (1ull << 13),
|
||||
SymbolInfoIncludeReferences = (1ull << 14),
|
||||
SymbolInfoIncludeBaseClasses = (1ull << 15),
|
||||
DeclarationOnly = (1ull << 16),
|
||||
DefinitionOnly = (1ull << 17),
|
||||
TargetUsrs = (1ull << 18),
|
||||
CursorKind = (1ull << 19),
|
||||
DisplayName = (1ull << 20),
|
||||
CompilationFlagsOnly = (1ull << 21),
|
||||
CompilationFlagsSplitLine = (1ull << 22),
|
||||
CompilationFlagsPwd = (1ull << 23),
|
||||
DumpIncludeHeaders = (1ull << 24),
|
||||
SilentQuery = (1ull << 25),
|
||||
SynchronousCompletions = (1ull << 26),
|
||||
NoSortReferencesByInput = (1ull << 27),
|
||||
HasLocation = (1ull << 28),
|
||||
WildcardSymbolNames = (1ull << 29),
|
||||
NoColor = (1ull << 30),
|
||||
Rename = (1ull << 31),
|
||||
ContainingFunction = (1ull << 32),
|
||||
ContainingFunctionLocation = (1ull << 33),
|
||||
DumpCheckIncludes = (1ull << 34),
|
||||
CurrentProjectOnly = (1ull << 35),
|
||||
Wait = (1ull << 36),
|
||||
CodeCompleteIncludeMacros = (1ull << 37),
|
||||
XML = (1ull << 38),
|
||||
NoSpellChecking = (1ull << 39),
|
||||
CodeCompleteIncludes = (1ull << 40),
|
||||
TokensIncludeSymbols = (1ull << 41),
|
||||
JSON = (1ull << 42),
|
||||
CodeCompletionEnabled = (1ull << 43),
|
||||
SynchronousDiagnostics = (1ull << 44),
|
||||
CodeCompleteNoWait = (1ull << 45),
|
||||
AllTargets = (1ull << 46)
|
||||
};
|
||||
|
||||
QueryMessage(Type type = Invalid);
|
||||
|
||||
Type type() const { return mType; }
|
||||
|
||||
struct KindFilters {
|
||||
enum Flag {
|
||||
None = 0x0,
|
||||
InHasWildcards = 0x1,
|
||||
InHasCategories = 0x2,
|
||||
OutHasWildcards = 0x4,
|
||||
OutHasCategories = 0x8
|
||||
};
|
||||
enum DefinitionType {
|
||||
Unset = 0x0,
|
||||
NotDefinition = 0x1,
|
||||
Definition = 0x2,
|
||||
Wildcard = 0x4,
|
||||
Category = 0x8
|
||||
};
|
||||
Flags<Flag> flags;
|
||||
Map<String, Flags<DefinitionType> > in, out;
|
||||
bool filter(const Symbol &symbol) const;
|
||||
void insert(const String &arg);
|
||||
bool isEmpty() const { return in.isEmpty() && out.isEmpty(); }
|
||||
};
|
||||
|
||||
struct PathFilter {
|
||||
String pattern;
|
||||
enum Mode {
|
||||
Self,
|
||||
Dependency
|
||||
} mode;
|
||||
|
||||
bool operator<(const PathFilter &other) const
|
||||
{
|
||||
const int cmp = pattern.compare(other.pattern);
|
||||
if (!cmp)
|
||||
return mode < other.mode;
|
||||
return cmp < 0;
|
||||
}
|
||||
};
|
||||
const List<PathFilter> &pathFilters() const { return mPathFilters; }
|
||||
void setPathFilters(const Set<PathFilter> &filters)
|
||||
{
|
||||
mPathFilters = filters.toList();
|
||||
std::sort(mPathFilters.begin(), mPathFilters.end());
|
||||
}
|
||||
|
||||
#ifdef RTAGS_HAS_LUA
|
||||
void setVisitASTScripts(const List<String> &scripts) { mVisitASTScripts = scripts; }
|
||||
List<String> visitASTScripts() const { return mVisitASTScripts; }
|
||||
#endif
|
||||
|
||||
void setKindFilters(const KindFilters &kindFilters) { mKindFilters = kindFilters; }
|
||||
const KindFilters &kindFilters() const { return mKindFilters; }
|
||||
|
||||
void setUnsavedFiles(const UnsavedFiles &unsavedFiles) { mUnsavedFiles = unsavedFiles; }
|
||||
const UnsavedFiles &unsavedFiles() const { return mUnsavedFiles; }
|
||||
|
||||
String query() const { return mQuery; }
|
||||
Location location(Location::DecodeFlag flag = Location::NoDecodeFlag) const
|
||||
{
|
||||
return Location::decode(mQuery, flag);
|
||||
}
|
||||
void setQuery(String &&query) { mQuery = std::move(query); }
|
||||
void setBuildIndex(int index) { mBuildIndex = index; }
|
||||
int buildIndex() const { return mBuildIndex; }
|
||||
|
||||
int terminalWidth() const { return mTerminalWidth; }
|
||||
void setTerminalWidth(int w) { mTerminalWidth = w; }
|
||||
|
||||
Match match() const;
|
||||
|
||||
void setRangeFilter(int min, int max)
|
||||
{
|
||||
mMinLine = min;
|
||||
mMaxLine = max;
|
||||
}
|
||||
|
||||
int minLine() const { return mMinLine; }
|
||||
int maxLine() const { return mMaxLine; }
|
||||
|
||||
int max() const { return mMax; }
|
||||
void setMax(int max) { mMax = max; }
|
||||
|
||||
Flags<Flag> flags() const { return mFlags; }
|
||||
void setFlags(Flags<Flag> flags)
|
||||
{
|
||||
mFlags = flags;
|
||||
switch (mType) {
|
||||
case ClearProjects:
|
||||
case Project:
|
||||
case DeleteProject:
|
||||
case Reindex:
|
||||
case RemoveFile:
|
||||
case Sources:
|
||||
mFlags |= MatchRegex;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setFlag(Flag flag, bool on = true) { mFlags.set(flag, on); }
|
||||
static Flag flagFromString(const String &string);
|
||||
static Flags<Location::ToStringFlag> locationToStringFlags(Flags<Flag> queryFlags);
|
||||
inline Flags<Location::ToStringFlag> locationToStringFlags() const { return locationToStringFlags(mFlags); }
|
||||
|
||||
virtual void encode(Serializer &serializer) const override;
|
||||
virtual void decode(Deserializer &deserializer) override;
|
||||
|
||||
void setCurrentFile(const Path ¤tFile) { mCurrentFile = currentFile; }
|
||||
Path currentFile() const { return mCurrentFile; }
|
||||
|
||||
String codeCompletePrefix() const { return mCodeCompletePrefix; }
|
||||
void setCodeCompletePrefix(String &&prefix) { mCodeCompletePrefix = std::move(prefix); }
|
||||
private:
|
||||
String mQuery, mCodeCompletePrefix;
|
||||
Type mType;
|
||||
Flags<QueryMessage::Flag> mFlags;
|
||||
int mMax, mMinLine, mMaxLine, mBuildIndex;
|
||||
List<PathFilter> mPathFilters;
|
||||
KindFilters mKindFilters;
|
||||
Path mCurrentFile;
|
||||
UnsavedFiles mUnsavedFiles;
|
||||
int mTerminalWidth;
|
||||
#ifdef RTAGS_HAS_LUA
|
||||
List<String> mVisitASTScripts;
|
||||
#endif
|
||||
};
|
||||
|
||||
DECLARE_NATIVE_TYPE(QueryMessage::Type);
|
||||
RCT_FLAGS(QueryMessage::Flag);
|
||||
RCT_FLAGS(QueryMessage::KindFilters::DefinitionType);
|
||||
RCT_FLAGS(QueryMessage::KindFilters::Flag);
|
||||
|
||||
inline Serializer &operator<<(Serializer &s, const QueryMessage::PathFilter &filter)
|
||||
{
|
||||
s << filter.pattern << static_cast<uint8_t>(filter.mode);
|
||||
return s;
|
||||
}
|
||||
|
||||
inline Deserializer &operator>>(Deserializer &s, QueryMessage::PathFilter &filter)
|
||||
{
|
||||
uint8_t mode;
|
||||
s >> filter.pattern >> mode;
|
||||
filter.mode = static_cast<QueryMessage::PathFilter::Mode>(mode);
|
||||
return s;
|
||||
}
|
||||
|
||||
inline Serializer &operator<<(Serializer &s, const QueryMessage::KindFilters &filter)
|
||||
{
|
||||
s << filter.flags << filter.in << filter.out;
|
||||
return s;
|
||||
}
|
||||
|
||||
inline Deserializer &operator>>(Deserializer &s, QueryMessage::KindFilters &filter)
|
||||
{
|
||||
s >> filter.flags >> filter.in >> filter.out;
|
||||
return s;
|
||||
}
|
||||
|
||||
#endif // QUERYMESSAGE_H
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,234 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef RClient_h
|
||||
#define RClient_h
|
||||
|
||||
#include "QueryMessage.h"
|
||||
#include "rct/List.h"
|
||||
#include "rct/Message.h"
|
||||
#include "rct/Path.h"
|
||||
#include "rct/Set.h"
|
||||
#include "rct/String.h"
|
||||
#include "CommandLineParser.h"
|
||||
#include "rct/rct-config.h"
|
||||
|
||||
class RCCommand;
|
||||
class QueryCommand;
|
||||
class Connection;
|
||||
class RClient
|
||||
{
|
||||
public:
|
||||
enum OptionType {
|
||||
None = 0,
|
||||
AbsolutePath,
|
||||
AddBuffers,
|
||||
AllDependencies,
|
||||
AllReferences,
|
||||
AllTargets,
|
||||
BuildIndex,
|
||||
CheckIncludes,
|
||||
CheckReindex,
|
||||
ClassHierarchy,
|
||||
Clear,
|
||||
CodeCompleteAt,
|
||||
CodeCompleteIncludeMacros,
|
||||
CodeCompleteIncludes,
|
||||
CodeCompleteNoWait,
|
||||
CodeCompletePrefix,
|
||||
CodeCompletionEnabled,
|
||||
CompilationFlagsOnly,
|
||||
CompilationFlagsPwd,
|
||||
CompilationFlagsSplitLine,
|
||||
Compile,
|
||||
ConnectTimeout,
|
||||
ContainingFunction,
|
||||
ContainingFunctionLocation,
|
||||
CurrentFile,
|
||||
CurrentProject,
|
||||
CursorKind,
|
||||
DebugLocations,
|
||||
DeclarationOnly,
|
||||
DefinitionOnly,
|
||||
DeleteProject,
|
||||
Dependencies,
|
||||
DependencyFilter,
|
||||
Diagnose,
|
||||
DiagnoseAll,
|
||||
Diagnostics,
|
||||
DisplayName,
|
||||
DumpCompileCommands,
|
||||
DumpCompletions,
|
||||
DumpFile,
|
||||
DumpFileMaps,
|
||||
DumpIncludeHeaders,
|
||||
Elisp,
|
||||
FilterSystemHeaders,
|
||||
FindFile,
|
||||
FindFilePreferExact,
|
||||
FindProjectBuildRoot,
|
||||
FindProjectRoot,
|
||||
FindSymbols,
|
||||
FindVirtuals,
|
||||
FixIts,
|
||||
FollowLocation,
|
||||
GenerateTest,
|
||||
GuessFlags,
|
||||
HasFileManager,
|
||||
Help,
|
||||
IncludeFile,
|
||||
IsIndexed,
|
||||
IsIndexing,
|
||||
JSON,
|
||||
JobCount,
|
||||
KindFilter,
|
||||
LastIndexed,
|
||||
ListBuffers,
|
||||
ListCursorKinds,
|
||||
ListSymbols,
|
||||
LoadCompileCommands,
|
||||
LogFile,
|
||||
Man,
|
||||
MatchCaseInsensitive,
|
||||
MatchRegex,
|
||||
Max,
|
||||
NoColor,
|
||||
NoContext,
|
||||
NoRealPath,
|
||||
NoSortReferencesByInput,
|
||||
NoSpellCheckinging,
|
||||
Noop,
|
||||
PathFilter,
|
||||
PreprocessFile,
|
||||
Project,
|
||||
ProjectRoot,
|
||||
QuitRdm,
|
||||
RTagsConfig,
|
||||
RangeFilter,
|
||||
RdmLog,
|
||||
ReferenceLocation,
|
||||
ReferenceName,
|
||||
Reindex,
|
||||
ReloadFileManager,
|
||||
RemoveBuffers,
|
||||
RemoveFile,
|
||||
Rename,
|
||||
ReverseSort,
|
||||
SendDiagnostics,
|
||||
SetBuffers,
|
||||
Silent,
|
||||
SilentQuery,
|
||||
SocketAddress,
|
||||
SocketFile,
|
||||
Sources,
|
||||
Status,
|
||||
StripParen,
|
||||
Suspend,
|
||||
SymbolInfo,
|
||||
SymbolInfoIncludeBaseClasses,
|
||||
SymbolInfoIncludeParents,
|
||||
SymbolInfoIncludeReferences,
|
||||
SymbolInfoIncludeTargets,
|
||||
SynchronousCompletions,
|
||||
SynchronousDiagnostics,
|
||||
TargetUsrs,
|
||||
Timeout,
|
||||
Tokens,
|
||||
TokensIncludeSymbols,
|
||||
UnsavedFile,
|
||||
Validate,
|
||||
Verbose,
|
||||
VerifyVersion,
|
||||
Version,
|
||||
VisitAST,
|
||||
VisitASTScript,
|
||||
Wait,
|
||||
WildcardSymbolNames,
|
||||
XML,
|
||||
NumOptions
|
||||
};
|
||||
|
||||
RClient();
|
||||
~RClient();
|
||||
void exec();
|
||||
int exitCode() const { return mExitCode; }
|
||||
CommandLineParser::ParseStatus parse(size_t argc, char **argv);
|
||||
|
||||
int max() const { return mMax; }
|
||||
LogLevel logLevel() const { return mLogLevel; }
|
||||
int timeout() const { return mTimeout; }
|
||||
int buildIndex() const { return mBuildIndex; }
|
||||
|
||||
const Set<QueryMessage::PathFilter> &pathFilters() const { return mPathFilters; }
|
||||
int minOffset() const { return mMinOffset; }
|
||||
int maxOffset() const { return mMaxOffset; }
|
||||
|
||||
const QueryMessage::KindFilters &kindFilters() const { return mKindFilters; }
|
||||
|
||||
const UnsavedFiles &unsavedFiles() const { return mUnsavedFiles; }
|
||||
|
||||
const List<String> &rdmArgs() const { return mRdmArgs; }
|
||||
const Path ¤tFile() const { return mCurrentFile; }
|
||||
|
||||
String socketFile() const { return mSocketFile; }
|
||||
String tcpHost() const { return mTcpHost; }
|
||||
uint16_t tcpPort() const { return mTcpPort; }
|
||||
Path projectRoot() const { return mProjectRoot; }
|
||||
Flags<QueryMessage::Flag> queryFlags() const { return mQueryFlags; }
|
||||
int terminalWidth() const { return mTerminalWidth; }
|
||||
|
||||
String commandLine() const { return mCommandLine; }
|
||||
void onNewMessage(const std::shared_ptr<Message> &message, const std::shared_ptr<Connection> &);
|
||||
List<String> environment() const;
|
||||
String codeCompletePrefix() const { return mCodeCompletePrefix; }
|
||||
#ifdef RTAGS_HAS_LUA
|
||||
List<String> visitASTScripts() const { return mVisitASTScripts; }
|
||||
#endif
|
||||
private:
|
||||
void addQuery(QueryMessage::Type t, String &&query = String(),
|
||||
Flags<QueryMessage::Flag> extraQueryFlags = Flags<QueryMessage::Flag>());
|
||||
void addQuitCommand(int exitCode);
|
||||
|
||||
void addLog(LogLevel level);
|
||||
void addCompile(String &&args, const Path &cwd);
|
||||
void addCompile(Path &&compileCommands);
|
||||
|
||||
Flags<QueryMessage::Flag> mQueryFlags;
|
||||
int mMax, mTimeout, mMinOffset, mMaxOffset, mConnectTimeout, mBuildIndex;
|
||||
LogLevel mLogLevel;
|
||||
Set<QueryMessage::PathFilter> mPathFilters;
|
||||
QueryMessage::KindFilters mKindFilters;
|
||||
UnsavedFiles mUnsavedFiles;
|
||||
List<std::shared_ptr<RCCommand> > mCommands;
|
||||
List<String> mRdmArgs;
|
||||
Path mSocketFile;
|
||||
Path mCurrentFile;
|
||||
String mTcpHost;
|
||||
String mCodeCompletePrefix;
|
||||
uint16_t mTcpPort;
|
||||
bool mGuessFlags;
|
||||
Path mProjectRoot;
|
||||
int mTerminalWidth;
|
||||
int mExitCode;
|
||||
#ifdef RTAGS_HAS_LUA
|
||||
List<String> mVisitASTScripts;
|
||||
#endif
|
||||
mutable List<String> mEnvironment;
|
||||
|
||||
String mCommandLine;
|
||||
friend class CompileCommand;
|
||||
};
|
||||
|
||||
#endif
|
||||
-1278
File diff suppressed because it is too large.
Load diff
-1225
File diff suppressed because it is too large.
Load diff
@@ -1,70 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef RTagsLogOutput_h
|
||||
#define RTagsLogOutput_h
|
||||
|
||||
#include "rct/Connection.h"
|
||||
#include "rct/Log.h"
|
||||
#include "rct/String.h"
|
||||
|
||||
class RTagsLogOutput : public LogOutput
|
||||
{
|
||||
public:
|
||||
enum Flag {
|
||||
None = 0x0,
|
||||
Elisp = 0x1,
|
||||
XML = 0x2,
|
||||
JSON = 0x4,
|
||||
NoSpellChecking = 0x8
|
||||
};
|
||||
RTagsLogOutput(LogLevel level, unsigned int flags, const std::shared_ptr<Connection> &conn = std::shared_ptr<Connection>())
|
||||
: LogOutput(level), mFlags(flags), mConnection(conn)
|
||||
{
|
||||
if (conn) {
|
||||
conn->disconnected().connect(std::bind(&RTagsLogOutput::remove, this));
|
||||
conn->client()->setLogsEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
virtual unsigned int flags() const override { return mFlags; }
|
||||
|
||||
virtual bool testLog(LogLevel level) const override
|
||||
{
|
||||
if (level == RTags::DiagnosticsLevel && mFlags & NoSpellChecking)
|
||||
return false;
|
||||
if (logLevel() < LogLevel::Error || level < LogLevel::Error)
|
||||
return level == logLevel();
|
||||
return LogOutput::testLog(level);
|
||||
}
|
||||
virtual void log(Flags<LogFlag>, const char *msg, int len) override
|
||||
{
|
||||
if (mConnection) {
|
||||
std::shared_ptr<EventLoop> main = EventLoop::mainEventLoop();
|
||||
if (EventLoop::eventLoop() == main) {
|
||||
mConnection->write(String(msg, len));
|
||||
} else {
|
||||
EventLoop::mainEventLoop()->callLaterMove(std::bind((bool(Connection::*)(Message&&))&Connection::send, mConnection, std::placeholders::_1),
|
||||
ResponseMessage(String(msg, len)));
|
||||
}
|
||||
}
|
||||
}
|
||||
std::shared_ptr<Connection> connection() const { return mConnection; }
|
||||
private:
|
||||
const unsigned int mFlags;
|
||||
std::shared_ptr<Connection> mConnection;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,52 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef RTagsMessage_h
|
||||
#define RTagsMessage_h
|
||||
|
||||
#include "rct/Message.h"
|
||||
#include "rct/String.h"
|
||||
#include <cstdint>
|
||||
#include <string.h>
|
||||
|
||||
class RTagsMessage : public Message
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
CompletionId = 32,
|
||||
QueryId,
|
||||
CompileId,
|
||||
LogOutputId,
|
||||
VisitFileResponseId,
|
||||
VisitFileId,
|
||||
IndexDataMessageId,
|
||||
JobRequestId,
|
||||
JobResponseId,
|
||||
JobAnnouncementId,
|
||||
ProxyJobAnnouncementId,
|
||||
ClientMessageId,
|
||||
ClientConnectedId,
|
||||
ExitMessageId
|
||||
};
|
||||
|
||||
RTagsMessage(uint8_t id) : Message(id) {}
|
||||
|
||||
inline void setCommandLine(const String &cmd) { mCommandLine = cmd; }
|
||||
inline String commandLine() const { return mCommandLine; }
|
||||
protected:
|
||||
String mCommandLine;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace RTags {
|
||||
|
||||
enum {
|
||||
MajorVersion = @RTAGS_VERSION_MAJOR@,
|
||||
MinorVersion = @RTAGS_VERSION_MINOR@,
|
||||
DatabaseVersion = @RTAGS_VERSION_DATABASE@,
|
||||
SourcesFileVersion = @RTAGS_VERSION_SOURCES_FILE@
|
||||
};
|
||||
}
|
||||
@@ -1,272 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "ReferencesJob.h"
|
||||
|
||||
#include "Project.h"
|
||||
#include "RTags.h"
|
||||
#include "Server.h"
|
||||
|
||||
static inline Flags<QueryJob::JobFlag> jobFlags(Flags<QueryMessage::Flag> queryFlags)
|
||||
{
|
||||
return (queryFlags & QueryMessage::Elisp ? Flags<QueryJob::JobFlag>(QueryJob::QuoteOutput) : Flags<QueryJob::JobFlag>());
|
||||
}
|
||||
|
||||
ReferencesJob::ReferencesJob(Location loc, const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Project> &proj)
|
||||
: QueryJob(query, proj, ::jobFlags(query->flags()))
|
||||
{
|
||||
mLocations.insert(loc);
|
||||
}
|
||||
|
||||
ReferencesJob::ReferencesJob(const String &sym, const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Project> &proj)
|
||||
: QueryJob(query, proj, ::jobFlags(query->flags())), mSymbolName(sym)
|
||||
{
|
||||
}
|
||||
|
||||
int ReferencesJob::execute()
|
||||
{
|
||||
const bool rename = queryFlags() & QueryMessage::Rename;
|
||||
std::shared_ptr<Project> proj = project();
|
||||
if (!proj)
|
||||
return 1;
|
||||
Set<Symbol> refs;
|
||||
Map<Location, std::pair<bool, CXCursorKind> > references;
|
||||
if (!mSymbolName.isEmpty()) {
|
||||
const bool hasFilter = QueryJob::hasFilter();
|
||||
auto inserter = [this, hasFilter](Project::SymbolMatchType type, const String &string, const Set<Location> &locs) {
|
||||
if (type == Project::StartsWith) {
|
||||
const size_t paren = string.indexOf('(');
|
||||
if (paren == String::npos || paren != mSymbolName.size() || RTags::isFunctionVariable(string))
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &l : locs) {
|
||||
if (!hasFilter || filter(l.path())) {
|
||||
mLocations.insert(l);
|
||||
}
|
||||
}
|
||||
};
|
||||
proj->findSymbols(mSymbolName, inserter, queryFlags());
|
||||
}
|
||||
const bool declarationOnly = queryFlags() & QueryMessage::DeclarationOnly;
|
||||
const bool definitionOnly = queryFlags() & QueryMessage::DefinitionOnly;
|
||||
Location startLocation;
|
||||
bool first = true;
|
||||
for (auto it = mLocations.begin(); it != mLocations.end(); ++it) {
|
||||
const Location pos = *it;
|
||||
Symbol sym = proj->findSymbol(pos);
|
||||
if (sym.isNull())
|
||||
continue;
|
||||
if (first && !(queryFlags() & QueryMessage::NoSortReferencesByInput)) {
|
||||
first = false;
|
||||
startLocation = sym.location;
|
||||
}
|
||||
|
||||
if (sym.isReference()) {
|
||||
const Symbol target = proj->findTarget(sym);
|
||||
if (!target.isNull() && target.kind != CXCursor_MacroExpansion)
|
||||
sym = target;
|
||||
}
|
||||
if (sym.isNull())
|
||||
continue;
|
||||
|
||||
if (rename && sym.isConstructorOrDestructor()) {
|
||||
const Location loc = sym.location;
|
||||
sym.clear();
|
||||
const Set<String> usrs = proj->findTargetUsrs(loc);
|
||||
for (const String &usr : usrs) {
|
||||
for (const Symbol &s : proj->findByUsr(usr, loc.fileId(), Project::All)) {
|
||||
if (s.isClass()) {
|
||||
sym = s;
|
||||
if (s.isDefinition())
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sym.isNull())
|
||||
continue;
|
||||
}
|
||||
if (queryFlags() & QueryMessage::AllReferences) {
|
||||
const Set<Symbol> all = proj->findAllReferences(sym);
|
||||
for (const auto &symbol : all) {
|
||||
if (rename) {
|
||||
if (symbol.kind == CXCursor_MacroExpansion && sym.kind != CXCursor_MacroDefinition) {
|
||||
continue;
|
||||
}
|
||||
} else if (sym.isClass() && symbol.isConstructorOrDestructor()) {
|
||||
continue;
|
||||
}
|
||||
const bool def = symbol.isDefinition();
|
||||
if (def) {
|
||||
if (declarationOnly)
|
||||
continue;
|
||||
} else if (definitionOnly) {
|
||||
continue;
|
||||
}
|
||||
references[symbol.location] = std::make_pair(def, symbol.kind);
|
||||
}
|
||||
} else if (queryFlags() & QueryMessage::FindVirtuals) {
|
||||
const Set<Symbol> virtuals = proj->findVirtuals(sym);
|
||||
for (const auto &symbol : virtuals) {
|
||||
const bool def = symbol.isDefinition();
|
||||
if (def) {
|
||||
if (declarationOnly)
|
||||
continue;
|
||||
} else if (definitionOnly) {
|
||||
continue;
|
||||
}
|
||||
references[symbol.location] = std::make_pair(def, symbol.kind);
|
||||
}
|
||||
} else {
|
||||
const Set<Symbol> symbols = proj->findCallers(sym);
|
||||
for (const auto &symbol : symbols) {
|
||||
const bool def = symbol.isDefinition();
|
||||
if (def) {
|
||||
if (declarationOnly)
|
||||
continue;
|
||||
} else if (definitionOnly) {
|
||||
continue;
|
||||
}
|
||||
references[symbol.location] = std::make_pair(false, CXCursor_FirstInvalid);
|
||||
}
|
||||
}
|
||||
}
|
||||
Flags<QueryJob::WriteFlag> writeFlags;
|
||||
if (queryFlags() & QueryMessage::Elisp) {
|
||||
write("(list ", DontQuote);
|
||||
writeFlags |= QueryJob::NoContext;
|
||||
} else if (queryFlags() & QueryMessage::NoContext) {
|
||||
writeFlags |= QueryJob::NoContext;
|
||||
}
|
||||
|
||||
auto writeCons = [this](const String &car, const String &cdr) {
|
||||
write("(cons ", DontQuote);
|
||||
write(car, DontQuote);
|
||||
write(cdr);
|
||||
write(")", DontQuote);
|
||||
};
|
||||
|
||||
Value json;
|
||||
auto writeLoc = [this, writeCons, writeFlags, &json](Location loc) {
|
||||
if (queryFlags() & QueryMessage::Elisp) {
|
||||
if (!filterLocation(loc))
|
||||
return;
|
||||
write("(list ", DontQuote);
|
||||
locationToString(loc, [writeCons, this](LocationPiece piece, const String &string) {
|
||||
switch (piece) {
|
||||
case Piece_ContainingFunctionLocation:
|
||||
if (queryFlags() & QueryMessage::ContainingFunctionLocation)
|
||||
writeCons("'cfl", string);
|
||||
break;
|
||||
case Piece_ContainingFunctionName:
|
||||
if (queryFlags() & QueryMessage::ContainingFunction)
|
||||
writeCons("'cf", string);
|
||||
break;
|
||||
case Piece_Location:
|
||||
writeCons("'loc", string);
|
||||
break;
|
||||
case Piece_Context:
|
||||
if (!(queryFlags() & QueryMessage::NoContext))
|
||||
writeCons("'ctx", string);
|
||||
break;
|
||||
case Piece_SymbolName:
|
||||
case Piece_Kind:
|
||||
break;
|
||||
}
|
||||
});
|
||||
write(")", DontQuote);
|
||||
} else if (queryFlags() & QueryMessage::JSON) {
|
||||
if (!filterLocation(loc))
|
||||
return;
|
||||
Value value;
|
||||
locationToString(loc, [&value, this](LocationPiece piece, const String &string) {
|
||||
switch (piece) {
|
||||
case Piece_ContainingFunctionLocation:
|
||||
if (queryFlags() & QueryMessage::ContainingFunctionLocation)
|
||||
value["cfl"] = string;
|
||||
break;
|
||||
case Piece_ContainingFunctionName:
|
||||
if (queryFlags() & QueryMessage::ContainingFunction)
|
||||
value["cf"] = string;
|
||||
break;
|
||||
case Piece_Location:
|
||||
value["loc"] = string;
|
||||
break;
|
||||
case Piece_Context:
|
||||
if (!(queryFlags() & QueryMessage::NoContext))
|
||||
value["ctx"] = string;
|
||||
break;
|
||||
case Piece_SymbolName:
|
||||
case Piece_Kind:
|
||||
break;
|
||||
}
|
||||
});
|
||||
json.push_back(value);
|
||||
} else {
|
||||
write(loc, writeFlags);
|
||||
}
|
||||
};
|
||||
|
||||
if (rename) {
|
||||
if (!references.isEmpty()) {
|
||||
if (queryFlags() & QueryMessage::ReverseSort) {
|
||||
Map<Location, std::pair<bool, CXCursorKind> >::const_iterator it = references.end();
|
||||
do {
|
||||
--it;
|
||||
writeLoc(it->first);
|
||||
} while (it != references.begin());
|
||||
} else {
|
||||
for (const auto &it : references) {
|
||||
writeLoc(it.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
List<RTags::SortedSymbol> sorted;
|
||||
sorted.reserve(references.size());
|
||||
for (Map<Location, std::pair<bool, CXCursorKind> >::const_iterator it = references.begin();
|
||||
it != references.end(); ++it) {
|
||||
sorted.append(RTags::SortedSymbol(it->first, it->second.first, it->second.second));
|
||||
}
|
||||
if (queryFlags() & QueryMessage::ReverseSort) {
|
||||
std::sort(sorted.begin(), sorted.end(), std::greater<RTags::SortedSymbol>());
|
||||
} else {
|
||||
std::sort(sorted.begin(), sorted.end());
|
||||
}
|
||||
int startIndex = 0;
|
||||
const int count = sorted.size();
|
||||
if (!startLocation.isNull()) {
|
||||
for (int i=0; i<count; ++i) {
|
||||
if (sorted.at(i).location == startLocation) {
|
||||
startIndex = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<count; ++i) {
|
||||
Location loc = sorted.at((startIndex + i) % count).location;
|
||||
writeLoc(loc);
|
||||
}
|
||||
}
|
||||
if (queryFlags() & QueryMessage::Elisp) {
|
||||
write(")", DontQuote);
|
||||
} else if (queryFlags() & QueryMessage::JSON) {
|
||||
write(json.toJSON(), DontQuote|Unfiltered);
|
||||
}
|
||||
|
||||
return references.isEmpty() ? 1 : 0;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/* This file is part of RTags (http://rtags.net).
|
||||
|
||||
RTags is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RTags is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RTags. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef ReferencesJob_h
|
||||
#define ReferencesJob_h
|
||||
|
||||
#include "Location.h"
|
||||
#include "QueryJob.h"
|
||||
#include "rct/String.h"
|
||||
|
||||
class SymbolInfo;
|
||||
class ReferencesJob : public QueryJob
|
||||
{
|
||||
public:
|
||||
ReferencesJob(Location location, const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Project> &project);
|
||||
ReferencesJob(const String &symbolName, const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Project> &project);
|
||||
protected:
|
||||
virtual int execute() override;
|
||||
private:
|
||||
Set<Location> mLocations;
|
||||
const String mSymbolName;
|
||||
};
|
||||
|
||||
#endif
|
||||
Loaded 100 of 225 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user