diff --git a/app/kdevelop! b/app/kdevelop! index 5d97a25664..825b8a8107 100755 --- a/app/kdevelop! +++ b/app/kdevelop! @@ -1,56 +1,57 @@ #!/bin/sh # This file is part of KDevelop # Copyright 2011 David Nolden # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library 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 # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public License # along with this library; see the file COPYING.LIB. If not, write to # the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. export KDEV_BASEDIR=$(dirname $0) export APPLICATION=KDevelop if ! [ "$KDEV_ATTACHED_PID" ]; then ARGS="" if [ $# -eq 0 ]; then ARGS="--pss" else ARGS="$@" fi export KDEV_ATTACHED_PID=$($KDEV_BASEDIR/kdevelop $ARGS --pid) fi if ! [ "$KDEV_ATTACHED_PID" ]; then echo "Error: Failed retrieving a pid." exit 5 fi # Make sure we didn't capture garbage instead of the pid (must be 1 word only) # note: keep compatible on FreeBSD: https://bugs.kde.org/show_bug.cgi?id=311186 if [ "$(echo $KDEV_ATTACHED_PID | wc -w | sed 's/ //g')" != 1 ]; then echo "Error: Got no valid pid." exit 6 fi -if ! [ "$(which qdbus)" ]; then - echo "Error: The qdbus utility is missing." +# find qdbus, try also alternative name qdbus-qt5 +if ! [ "$(which qdbus-qt5 2> /dev/null)$(which qdbus 2> /dev/null)" ]; then + echo "Error: The qdbus (or qdbus-qt5) utility is missing." exit 7 fi export KDEV_DBUS_ID="org.kdevelop.kdevelop-$KDEV_ATTACHED_PID" case $(basename $SHELL) in zsh) OLD_ZDOTDIR="${ZDOTDIR-$HOME}" ZDOTDIR="$KDEV_BASEDIR/../share/kdevplatform/shellutils/" zsh -i ;; *) bash --init-file "$KDEV_BASEDIR/kdevplatform_shell_environment.sh" -i ;; esac diff --git a/cmake/modules/FindClang.cmake b/cmake/modules/FindClang.cmake index e40249ac29..c7498feece 100644 --- a/cmake/modules/FindClang.cmake +++ b/cmake/modules/FindClang.cmake @@ -1,118 +1,135 @@ # Detect Clang libraries # # Defines the following variables: # CLANG_FOUND - True if Clang was found # CLANG_INCLUDE_DIRS - Where to find Clang includes # CLANG_LIBRARY_DIRS - Where to find Clang libraries +# CLANG_BUILTIN_DIR - Where to find Clang builtin includes # # CLANG_CLANG_LIB - Libclang C library # # CLANG_CLANGFRONTEND_LIB - Clang Frontend (C++) Library # CLANG_CLANGDRIVER_LIB - Clang Driver (C++) Library # ... # # CLANG_LIBS - All the Clang C++ libraries # # Uses the same include and library paths detected by FindLLVM.cmake # # See http://clang.llvm.org/docs/InternalsManual.html for full list of libraries #============================================================================= # Copyright 2014-2015 Kevin Funk # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= set(KNOWN_VERSIONS 8 7 6.0 5.0 4.0 3.9 3.8) foreach(version ${KNOWN_VERSIONS}) if (LLVM_DIR OR (DEFINED Clang_FIND_VERSION AND Clang_FIND_VERSION VERSION_GREATER version)) break() endif () if (${Clang_FIND_REQUIRED}) find_package(LLVM ${version} REQUIRED) else () find_package(LLVM ${version}) endif () endforeach() set(CLANG_FOUND FALSE) if (LLVM_FOUND AND LLVM_LIBRARY_DIRS) macro(FIND_AND_ADD_CLANG_LIB _libname_) string(TOUPPER ${_libname_} _prettylibname_) find_library(CLANG_${_prettylibname_}_LIB NAMES ${_libname_} HINTS ${LLVM_LIBRARY_DIRS} ${ARGN}) if (CLANG_${_prettylibname_}_LIB) set(CLANG_LIBS ${CLANG_LIBS} ${CLANG_${_prettylibname_}_LIB}) endif() endmacro(FIND_AND_ADD_CLANG_LIB) FIND_AND_ADD_CLANG_LIB(clangFrontend) # note: On Windows there's 'libclang.dll' instead of 'clang.dll' -> search for 'libclang', too FIND_AND_ADD_CLANG_LIB(clang NAMES clang libclang) # LibClang: high-level C interface FIND_AND_ADD_CLANG_LIB(clangDriver) FIND_AND_ADD_CLANG_LIB(clangCodeGen) FIND_AND_ADD_CLANG_LIB(clangSema) FIND_AND_ADD_CLANG_LIB(clangChecker) FIND_AND_ADD_CLANG_LIB(clangAnalysis) FIND_AND_ADD_CLANG_LIB(clangRewriteFrontend) FIND_AND_ADD_CLANG_LIB(clangRewrite) FIND_AND_ADD_CLANG_LIB(clangAST) FIND_AND_ADD_CLANG_LIB(clangParse) FIND_AND_ADD_CLANG_LIB(clangLex) FIND_AND_ADD_CLANG_LIB(clangBasic) FIND_AND_ADD_CLANG_LIB(clangARCMigrate) FIND_AND_ADD_CLANG_LIB(clangEdit) FIND_AND_ADD_CLANG_LIB(clangFrontendTool) FIND_AND_ADD_CLANG_LIB(clangSerialization) FIND_AND_ADD_CLANG_LIB(clangTooling) FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerCheckers) FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerCore) FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerFrontend) FIND_AND_ADD_CLANG_LIB(clangRewriteCore) endif() if(CLANG_LIBS OR CLANG_CLANG_LIB) set(CLANG_FOUND TRUE) else() message(STATUS "Could not find any Clang libraries in ${LLVM_LIBRARY_DIRS}") endif() if(CLANG_FOUND) set(CLANG_LIBRARY_DIRS ${LLVM_LIBRARY_DIRS}) set(CLANG_INCLUDE_DIRS ${LLVM_INCLUDE_DIRS}) set(CLANG_VERSION ${LLVM_VERSION}) + find_path(CLANG_BUILTIN_DIR + # cpuid.h because it is defined in ClangSupport constructor as valid clang builtin dir indicator + NAMES "cpuid.h" + PATHS "${CLANG_LIBRARY_DIRS}" + "${CLANG_INCLUDE_DIRS}" + PATH_SUFFIXES "clang/${CLANG_VERSION}/include" + "../../../clang/${CLANG_VERSION}/include" + NO_DEFAULT_PATH + ) + + if (NOT CLANG_BUILTIN_DIR) + message(FATAL_ERROR "Could not find Clang builtin directory") + endif() + get_filename_component(CLANG_BUILTIN_DIR ${CLANG_BUILTIN_DIR} ABSOLUTE) + # check whether llvm-config comes from an install prefix execute_process( COMMAND ${LLVM_CONFIG_EXECUTABLE} --src-root OUTPUT_VARIABLE _llvmSourceRoot OUTPUT_STRIP_TRAILING_WHITESPACE ) string(FIND "${LLVM_INCLUDE_DIRS}" "${_llvmSourceRoot}" _llvmIsInstalled) if (NOT _llvmIsInstalled) message(STATUS "Detected that llvm-config comes from a build-tree, adding more include directories for Clang") list(APPEND CLANG_INCLUDE_DIRS "${LLVM_INSTALL_PREFIX}/tools/clang/include" # build dir "${_llvmSourceRoot}/tools/clang/include" # source dir ) endif() message(STATUS "Found Clang (LLVM version: ${CLANG_VERSION})") - message(STATUS " Include dirs: ${CLANG_INCLUDE_DIRS}") - message(STATUS " Clang libraries: ${CLANG_LIBS}") - message(STATUS " Libclang C library: ${CLANG_CLANG_LIB}") + message(STATUS " Include dirs: ${CLANG_INCLUDE_DIRS}") + message(STATUS " Clang libraries: ${CLANG_LIBS}") + message(STATUS " Libclang C library: ${CLANG_CLANG_LIB}") + message(STATUS " Builtin include dir: ${CLANG_BUILTIN_DIR}") else() if(Clang_FIND_REQUIRED) message(FATAL_ERROR "Could NOT find Clang") endif() endif() diff --git a/kdevplatform/util/kdevplatform_shell_environment.sh b/kdevplatform/util/kdevplatform_shell_environment.sh index 54f2261875..24699a8087 100755 --- a/kdevplatform/util/kdevplatform_shell_environment.sh +++ b/kdevplatform/util/kdevplatform_shell_environment.sh @@ -1,821 +1,830 @@ #!/bin/bash # This file is part of KDevelop # Copyright 2011 David Nolden # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library 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 # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public License # along with this library; see the file COPYING.LIB. If not, write to # the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301, USA. if [ -z "$ZDOTDIR" ]; then _shell=bash; else _shell=zsh; fi # Since this runs as a replacement for the init-files, we need to chain in the 'real' rcs. # We ignore profile, login & logout rcs, as we want no login shells. case $_shell in zsh) alias shopt=':' test -f "$OLD_ZDOTDIR/.zshenv" && . "$OLD_ZDOTDIR/.zshenv" test -f "$OLD_ZDOTDIR/.zshrc" && . "$OLD_ZDOTDIR/.zshrc" ;; #zsh still also sources the systemwide rcs when called with $ZDOTDIR set. bash) test -f ~/.bash_profile && source ~/.bash_profile test -f /etc/bash.bashrc && source /etc/bash.bashrc test -f ~/.bashrc && source ~/.bashrc ;; esac if ! [ "$APPLICATION_HOST" ]; then export APPLICATION_HOST=$(hostname) fi if ! [ "$KDEV_SHELL_ENVIRONMENT_ID" ]; then export KDEV_SHELL_ENVIRONMENT_ID="default" fi if ! [ "$KDEV_DBUS_ID" ]; then echo "The required environment variable KDEV_DBUS_ID is not set. This variable defines the dbus id of the application instance instance which is supposed to be attached." exit 5 fi # Eventually, if we are forwarding to another host, and kdevplatform_shell_environment.sh # has been located through "which kdevplatform_shell_environment.sh", then we need to update KDEV_BASEDIR. if ! [ -e "$KDEV_BASEDIR/kdevplatform_shell_environment.sh" ]; then KDEV_BASEDIR=$(dirname $(which kdevplatform_shell_environment.sh)) fi if ! [ -e "$KDEV_BASEDIR/kdev_dbus_socket_transformer" ]; then echo "The $KDEV_BASEDIR/kdev_dbus_socket_transformer utility is missing, controlling the application across ssh is not possible" fi # Takes a list of tools, and prints a warning of one of them is not available in the path function checkToolsInPath { for TOOL in $@; do if ! [ "$(which $TOOL 2> /dev/null)" ]; then echo "The utility $TOOL is not in your path, the shell integration will not work properly." fi done } # Check if all required tools are there (on the host machine) -checkToolsInPath sed qdbus ls cut dirname mktemp basename readlink hostname +checkToolsInPath sed ls cut dirname mktemp basename readlink hostname + +# special handling for qdbus variants +_qdbus=qdbus-qt5 +if ! [ "$(which $_qdbus 2> /dev/null)" ]; then + _qdbus=qdbus + if ! [ "$(which $_qdbus 2> /dev/null)" ]; then + echo "The utility qdbus (or qdbus-qt5) is not in your path, the shell integration will not work properly." + fi +fi if ! [ "$KDEV_SSH_FORWARD_CHAIN" ]; then # Check for additional utilities that are required on the client machine checkToolsInPath kioclient5 fi # Queries the session name from the running application instance function getSessionName { - echo "$(qdbus $KDEV_DBUS_ID /org/kdevelop/SessionController org.kdevelop.SessionController.sessionName)" + echo "$($_qdbus $KDEV_DBUS_ID /org/kdevelop/SessionController org.kdevelop.SessionController.sessionName)" } function getSessionDir { - echo "$(qdbus $KDEV_DBUS_ID /org/kdevelop/SessionController org.kdevelop.SessionController.sessionDir)" + echo "$($_qdbus $KDEV_DBUS_ID /org/kdevelop/SessionController org.kdevelop.SessionController.sessionDir)" } function getCurrentShellEnvPath { local ENV_ID=$KDEV_SHELL_ENVIRONMENT_ID if [ "$1" ]; then ENV_ID=$1 fi echo "$(getSessionDir)/${ENV_ID}.sh" } function help! { echo "You are controlling the $APPLICATION session '$(getSessionName)'" echo "" if [[ "$1" == "" ]]; then echo "Standard commands:" echo "raise! - Raise the window." echo "sync! - Synchronize the working directory with the currently open document. See \"help! sync\"" echo "open! [file] ... - Open the file(s) within the attached application. See \"help! open\"" echo "eopen! [file] ... - Open the file(s) within an external application using kde-open." echo "create! [file] [[text]] - Create and open a new file." echo "search! [pattern] [[locations]] ... - Search for the given pattern here or at the optionally given location(s)." echo "dsearch! [pattern] [[locations]] ... - Same as search, but starts the search instantly instead of showing the dialog (using previous settings)." echo "ssh! [ssh arguments] - Connect to a remote host via ssh, keeping the control-connection alive. See \"help! remote\"" echo "" echo "help! - Show help." echo "help! open - Show extended help about file opening commands." echo "help! sync - Show extended help about path synchronization commands." echo "help! remote - Show extended help about remote shell-integration through ssh." echo "help! env - Show extended help about the environment." echo "" echo "Most commands can be abbreviated by the first character(s), eg. r! instead of raise!, and se! instead of search!." fi if [[ "$1" == "open" ]]; then echo "Extended opening:" echo "The open! command can also be used to open files in specific tool-view configurations, by adding split-separators:" echo "- Files around the / separator will be arranged horizontally by split-view." echo "- Files around the - separator will be arranged vertically by split-view." echo "- Parens [ ... ] can be used to disambiguate the hierarchy (there must be spaces between filename and paren)." echo "- If a file is missing around a separator, the currently active view is inserted into the position." echo "" echo "Examples:" echo "open! file1 / file2 - The active view is split horizontally." echo " file1 is opened in the left view, and file2 in the right view." echo "open! file1 / [ file2 - file3 ] - The active view is split horizontally, and the right split-view is split vertically." echo " file1 is opened in the left view, file2 in the right upper view, and file3 in the right lower view." echo "open! / file1 - The active view is split horizontally." echo " - The active document is kept in the left split-view, and file1 is opened in the right split-view." echo "" echo "Short forms: o! = open!, eo! = eopen!, c! = create!" fi if [[ "$1" == "sync" ]]; then echo "Extended syncing:" echo "sync! [[project-name]] - If no project-name is given, then the sync! command synchronizes to the currently active document." echo " If no document is active, then it synchronizes to the currently selected item in the project tree-view." echo " If a case-insensitive project name prefix is given, then it synchronizes to the base folder of the matching project." echo "syncsel! - Synchronizes to the currently selected item in the project tree-view, independently of the active document." echo "project! [[project-name]] - Map from a path within the build directory to the corresponding path in the source directory." echo " If we're already in the source directory, map to the root of the surrounding project." echo "bdir! [[project-name]] - Map from a path within the source directory to the corresponding path in the build directory." echo " If we're already in the build directory, map to the root of the build directory." echo "" echo "Short forms: s! = sync!, ss! = syncsel!, p! = project!, b! = bdir!" fi if [[ "$1" == "remote" ]]; then echo "Extended remote commands:" echo "ssh! [ssh arguments] - Connect to a remote host via ssh, keeping the control-connection alive." echo " - The whole dbus environment is forwarded, KDevelop needs to be installed on both sides." echo "ssw! [ssh arguments] - Like ssh!, but preserves the current working directory." echo "exec! [cmd] [args] [file] . .. - Execute the given command on the client machine, referencing any number of local files on the host machine." echo " - The file paths will be re-encoded as fish:// urls if required." echo "cexec! [cmd] [args] [file] . .. - Execute the given command on the client machine, referencing any number of local files on the host machine." echo " - The files will be COPIED to the client machine if required." echo "copytohost! [client path] [host path] - Copy a file/directory through the fish protocol from the client machine th the host machine." echo "copytoclient! [host path] [client path]- Copy a file/directory through the fish protocol from the host machine to the client machine." echo "" echo "Short forms: e! = exec!, ce! = cexec!, cth! = copytohost!, ctc! = copytoclient!" fi if [[ "$1" == "env" ]]; then echo "Environment management:" echo "The environment can be used to store session-specific macros and generally manipulate the shell environment" echo "for embedded shell sessions. The environment is sourced into the shell when the shell is initialized, and" echo "whenever setenv! is called." echo "" echo "env! - List all available shell environment-ids for this session." echo "setenv! [id] - Set the shell environmnet-id for this session to the given id, or update the current one." echo "editenv! [id] - Edit the current shell environment or the one with the optionally given id." echo "showenv! [id] - Show the current shell environment or the one with the optionally given id." echo "" echo "Short forms: sev! = setenv!, ee! = editenv!, shenv! = showenv!" fi echo "" } # Short versions of the commands: function r! { raise! $@ } function s! { sync! $@ } function ss! { syncsel! } function syncsel! { sync! '[selection]' } function p! { if [ "$@" ]; then s! $@ fi project! } function b! { if [ "$@" ]; then s! $@ fi bdir! } function o! { open! $@ } function eo! { eopen! $@ } function e! { exec! $@ } function ce! { cexec! $@ } function c! { create! $@ } function se! { search! $@ } function ds! { dsearch! $@ } function h! { help! $@ } function cth! { copytohost! $@ } function ctc! { copytoclient! $@ } function sev! { setenv! $@ } function ee! { editenv! $@ } function shev! { showenv! $@ } # Internals: # Opens a document in internally in the application function openDocument { - RESULT=$(qdbus $KDEV_DBUS_ID /org/kdevelop/DocumentController org.kdevelop.DocumentController.openDocumentSimple $1) + RESULT=$($_qdbus $KDEV_DBUS_ID /org/kdevelop/DocumentController org.kdevelop.DocumentController.openDocumentSimple $1) if [[ "$RESULT" != "true" ]]; then echo "Failed to open $1" fi } # Opens a document in internally in the application function openDocuments { if [[ $_shell == "zsh" ]]; then arr=(${=1}) else arr=("$1") fi - RESULT=$(qdbus $KDEV_DBUS_ID /org/kdevelop/DocumentController org.kdevelop.DocumentController.openDocumentsSimple "(" $arr ")") + RESULT=$($_qdbus $KDEV_DBUS_ID /org/kdevelop/DocumentController org.kdevelop.DocumentController.openDocumentsSimple "(" $arr ")") if [[ "$RESULT" != "true" ]]; then echo "Failed to open $1" fi } # Executes a command on the client machine using the custom-script integration. # First argument: The full command. Second argument: The working directory. function executeInApp { local CMD="$1" local WD=$2 if ! [ "$WD" ]; then WD=$(pwd) fi - RESULT=$(qdbus $KDEV_DBUS_ID /org/kdevelop/ExternalScriptPlugin org.kdevelop.ExternalScriptPlugin.executeCommand "$CMD" "$WD") + RESULT=$($_qdbus $KDEV_DBUS_ID /org/kdevelop/ExternalScriptPlugin org.kdevelop.ExternalScriptPlugin.executeCommand "$CMD" "$WD") if [[ "$RESULT" != "true" ]]; then echo "Execution failed" fi } # First argument: The full command. Second argument: The working directory. # Executes the command silently and synchronously, and returns the output function executeInAppSync { local CMD=$1 local WD=$2 if ! [ "$WD" ]; then WD=$(pwd) fi - RESULT=$(qdbus $KDEV_DBUS_ID /org/kdevelop/ExternalScriptPlugin org.kdevelop.ExternalScriptPlugin.executeCommandSync "$CMD" "$WD") + RESULT=$($_qdbus $KDEV_DBUS_ID /org/kdevelop/ExternalScriptPlugin org.kdevelop.ExternalScriptPlugin.executeCommandSync "$CMD" "$WD") echo "$RESULT" } # Getter functions: function getActiveDocument { - qdbus $KDEV_DBUS_ID /org/kdevelop/DocumentController org.kdevelop.DocumentController.activeDocumentPath $@ + $_qdbus $KDEV_DBUS_ID /org/kdevelop/DocumentController org.kdevelop.DocumentController.activeDocumentPath $@ } function getOpenDocuments { - qdbus $KDEV_DBUS_ID /org/kdevelop/DocumentController org.kdevelop.DocumentController.activeDocumentPaths + $_qdbus $KDEV_DBUS_ID /org/kdevelop/DocumentController org.kdevelop.DocumentController.activeDocumentPaths } function raise! { - qdbus $KDEV_DBUS_ID /kdevelop/MainWindow org.kdevelop.MainWindow.ensureVisible + $_qdbus $KDEV_DBUS_ID /kdevelop/MainWindow org.kdevelop.MainWindow.ensureVisible } function bdir! { - TARG=$(qdbus $KDEV_DBUS_ID /org/kdevelop/ProjectController org.kdevelop.ProjectController.mapSourceBuild "$(pwd)" false) + TARG=$($_qdbus $KDEV_DBUS_ID /org/kdevelop/ProjectController org.kdevelop.ProjectController.mapSourceBuild "$(pwd)" false) if [ "$TARG" ]; then cd $TARG else echo "Got no path" fi } function project! { - TARG=$(qdbus $KDEV_DBUS_ID /org/kdevelop/ProjectController org.kdevelop.ProjectController.mapSourceBuild "$(pwd)" true) + TARG=$($_qdbus $KDEV_DBUS_ID /org/kdevelop/ProjectController org.kdevelop.ProjectController.mapSourceBuild "$(pwd)" true) if [ "$TARG" ]; then cd $TARG else echo "Got no path" fi } # Main functions: function raise! { - qdbus $KDEV_DBUS_ID /kdevelop/MainWindow org.kdevelop.MainWindow.ensureVisible + $_qdbus $KDEV_DBUS_ID /kdevelop/MainWindow org.kdevelop.MainWindow.ensureVisible } function sync! { local P=$(getActiveDocument $@) if [ "$P" ]; then if [[ "$P" == fish://* ]]; then # This regular expression filters the user@host:port out of fish:///user@host:port/path/... LOGIN=$(echo $P | sed "s/fish\:\/\/*\([^\/]*\)\(\/.*\)/\1/") P_ON_HOST=$(echo $P | sed "s/fish\:\/\/*\([^\/]*\)\(\/.*\)/\2/") if [[ "$KDEV_SSH_FORWARD_CHAIN" == "$LOGIN" ]]; then P="$P_ON_HOST" else if [[ "$KDEV_SSH_FORWARD_CHAIN" == "" ]]; then # Try to ssh to the host machine # We need to split away the optional ":port" suffix, because the ssh command does not allow that syntax HOST=$(echo $LOGIN | cut --delimiter=':' -f 1) CMD="ssh!" if [[ "$LOGIN" == *:* ]]; then # If there is a port, extract it PORT=$(echo $LOGIN | cut --delimiter=':' -f 2) CMD="$CMD -p $PORT" fi CMD="$CMD $HOST" # Execute the ssh command echo "Executing $CMD" KDEV_WORKING_DIR="$(dirname $P_ON_HOST)" $CMD return else echo "Cannot synchronize the working directory, because the host-names do not match (app: $LOGIN, shell: $KDEV_SSH_FORWARD_CHAIN)" return fi fi elif [[ "$P" == file://* ]]; then P=$(echo $P | sed 's$^file://$$') elif [ "$KDEV_SSH_FORWARD_CHAIN" ]; then # This session is being forwarded to another machine, but the current document is not # However, we won't complain, because it's possible that the machines share the same file-system if [[ $(isEqualFileOnHostAndClient $P) != "yes" ]]; then echo "Cannot synchronize the working directory, because the file systems do not match" return fi fi [ -d "$P" ] || P=$(dirname "$P") cd "$P" else echo "Got no path" fi } # Take a path, and returns "yes" if the equal file is available on the host and the client # The check is performed by comparing inode-numbers function isEqualFileOnHostAndClient { function trimWhiteSpace() { echo $1 } FILE=$1 INODE_HOST=$(trimWhiteSpace $(ls --color=never -i $FILE | cut -d' ' -f1)) INODE_CLIENT=$(trimWhiteSpace $(executeInAppSync "ls --color=never -i $FILE | cut -d' ' -f1" "$(dirname $FILE)")) if [[ "$INODE_HOST" == "$INODE_CLIENT" ]]; then echo "yes" else echo "" fi } # Takes a relative file, returns an absolute file/url that should be valid on the client. function mapFileToClient { local RELATIVE_FILE=$1 FILE=$(readlink -f $RELATIVE_FILE) if ! [ -e "$FILE" ]; then # Try opening the file anyway, it might be an url or something else we don't understand here FILE=$RELATIVE_FILE else # We are referencing an absolute file, available on the file-system. if [ "$KDEV_SSH_FORWARD_CHAIN" ]; then # If we are forwarding, map it to the client somehow. if [[ "$(isEqualFileOnHostAndClient "$FILE")" != "yes" ]]; then # We can eventually map the file using the fish protocol FISH_HOST=$KDEV_SSH_FORWARD_CHAIN if [[ "$FISH_HOST" == *\,* ]]; then # Extracts everything before the first comma FISH_HOST=$(echo $FISH_HOST | sed 's/\([^,]*\),\(.*\)/\1/') echo "ssh chain is too long: $KDEV_SSH_FORWARD_CHAIN mapping anyway using $FISH_HOST" 1>&2 fi # Theoretically, we can only map through fish if the forward-chains contains no comma, which means that # we forward only once. Try anyway, there might be the same filesystem on the whole forward-chain. FILE="fish://$FISH_HOST$FILE" fi fi fi echo $FILE } function open! { NEWFILES="" for RELATIVE_FILE; do if [[ "$RELATIVE_FILE" == "/" || "$RELATIVE_FILE" == "-" ]]; then FILE=$RELATIVE_FILE else FILE=$(mapFileToClient $RELATIVE_FILE) fi NEWFILES="$NEWFILES $FILE" done openDocuments "$NEWFILES" } function eopen! { for RELATIVE_FILE; do FILE=$(mapFileToClient $RELATIVE_FILE) executeInApp "kde-open5 $FILE" done } function exec! { ARGS="" for RELATIVE_FILE; do if [[ "$ARGS" == "" ]]; then # Do not transform the command-name ARGS=$RELATIVE_FILE else FILE=$(mapFileToClient $RELATIVE_FILE) ARGS=$ARGS" "$FILE fi done echo "Executing: " $ARGS executeInApp "$ARGS" } function copytohost! { executeInApp "kioclient5 copy $1 $(mapFileToClient $2)" } function copytoclient! { executeInApp "kioclient5 copy $(mapFileToClient $1) $2" } function cexec! { ARGS="" PREFIX="" TMP=1 for RELATIVE_FILE; do if [[ "$ARGS" == "" ]]; then # Do not transform the command-name ARGS=$RELATIVE_FILE else FILE=$(mapFileToClient $RELATIVE_FILE) if [[ "$FILE" == fish://* ]]; then # Add a prefix to copy the file into a temporary file # Keep the baseline as suffix, so that applications can easily recognize the mimetype PREFIX+="FILE$TMP=\$(mktemp).$(basename $FILE); kioclient5 copy $FILE \$FILE$TMP;" # Use the temporary variable instead of the name FILE="\$FILE$TMP" TMP=$(($TMP+1)) fi ARGS=$ARGS" "$FILE fi done echo "Executing: " $ARGS executeInApp "$PREFIX $ARGS" } function create! { FILE=$(readlink -f $1) if ! [ "$FILE" ]; then echo "Error: Bad arguments." return 1 fi if [ -e "$FILE" ]; then echo "The file $FILE already exists" return 2 fi echo $2 > $FILE openDocument $(mapFileToClient $FILE) } function search! { PATTERN=$1 # if ! [ "$PATTERN" ]; then # echo "Error: No pattern given." # return 1 # fi LOCATION=$2 if ! [ "$LOCATION" ]; then LOCATION="." fi LOCATION=$(mapFileToClient $LOCATION) for LOC in $*; do if [[ "$LOC" == "$1" ]]; then continue; fi if [[ "$LOC" == "$2" ]]; then continue; fi LOCATION="$LOCATION;$(mapFileToClient $LOC)" done - qdbus $KDEV_DBUS_ID /org/kdevelop/GrepViewPlugin org.kdevelop.GrepViewPlugin.startSearch "$PATTERN" "$LOCATION" true + $_qdbus $KDEV_DBUS_ID /org/kdevelop/GrepViewPlugin org.kdevelop.GrepViewPlugin.startSearch "$PATTERN" "$LOCATION" true } function dsearch! { PATTERN=$1 if ! [ "$PATTERN" ]; then echo "Error: No pattern given." return 1 fi LOCATION=$2 if ! [ "$LOCATION" ]; then LOCATION="." fi LOCATION=$(mapFileToClient $LOCATION) for LOC in $*; do if [[ "$LOC" == "$1" ]]; then continue; fi if [[ "$LOC" == "$2" ]]; then continue; fi LOCATION="$LOCATION;$(mapFileToClient $LOC)" done - qdbus $KDEV_DBUS_ID /org/kdevelop/GrepViewPlugin org.kdevelop.GrepViewPlugin.startSearch "$PATTERN" "$LOCATION" false + $_qdbus $KDEV_DBUS_ID /org/kdevelop/GrepViewPlugin org.kdevelop.GrepViewPlugin.startSearch "$PATTERN" "$LOCATION" false } ##### SSH DBUS FORWARDING -------------------------------------------------------------------------------------------------------------------- DBUS_SOCKET_TRANSFORMER=$KDEV_BASEDIR/kdev_dbus_socket_transformer # We need this, to make sure that our forwarding-loops won't get out of control # This configures the shell to kill background jobs when it is terminated shopt -s huponexit export DBUS_ABSTRACT_SOCKET_TARGET_BASE_PATH=/tmp/dbus-forwarded-$USER-$APPLICATION_HOST export DBUS_FORWARDING_TCP_LOCAL_PORT=9000 export DBUS_FORWARDING_TCP_MAX_LOCAL_PORT=10000 export DBUS_ABSTRACT_SOCKET_TARGET_INDEX=1 export DBUS_ABSTRACT_SOCKET_MAX_TARGET_INDEX=1000 function getPortFromSSHCommand { # The port is given to ssh exclusively in the format "-p PORT" # This regular expression extracts the "4821" from "ssh -q bla1 -p 4821 bla2" local ARGS=$@ local RET=$(echo "$@" | sed "s/.*-p \+\([0-9]*\).*/\1/") if [[ "$ARGS" == "$RET" ]]; then # There was no match echo "" else echo ":$RET" fi } function getLoginFromSSHCommand { # The login name can be given to ssh in the format "-l NAME" # This regular expression extracts the "NAME" from "ssh -q bla1 -l NAME bla2" local ARGS=$@ local RET=$(echo "$ARGS" | sed "s/.*-l \+\([a-z,A-Z,_,0-9]*\).*/\1/") if [[ "$RET" == "$ARGS" || "$RET" == "" ]]; then # There was no match echo "" else echo "$RET@" fi } function getHostFromSSHCommand { # This regular expression extracts the "bla2" from "echo "ssh -q bla1 -p 4821 bla2" # Specifically, it finds the first argument which is not preceded by a "-x" parameter kind specification. local CLEANED="" local NEWCLEANED="$@" while [[ "$NEWCLEANED" != "$CLEANED" ]]; do CLEANED="$NEWCLEANED" # This expression removes one "-x ARG" parameter NEWCLEANED="$(echo $CLEANED | sed "s/\(.*\)\(-[a-z,A-Z] \+[a-z,0-9]*\)\ \(.*\)/\1\3/")" done # After cleaning, the result should only consist of the host-name followed by an optional command. # Select the host-name, by extracting the forst column. echo $CLEANED | cut --delimiter=" " -f 1 } function getSSHForwardOptionsFromCommand { HOST="$(getLoginFromSSHCommand "$@")$(getHostFromSSHCommand "$@")$(getPortFromSSHCommand "$@")" if [ "$KDEV_SSH_FORWARD_CHAIN" ]; then # We are already forwarding, so we deal with a chain of multiple ssh commands. # We still record it, although it's not sure if we can use it somehow. echo "KDEV_SSH_FORWARD_CHAIN=\"$KDEV_SSH_FORWARD_CHAIN,$HOST\""; else echo "KDEV_SSH_FORWARD_CHAIN=$HOST" fi } function getDBusAbstractSocketSuffix { # From something like DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-wYmSkVH7FE,guid=b214dad39e0292a4299778d64d761a5b # extract the /tmp/dbus-wYmSkVH7FE echo $DBUS_SESSION_BUS_ADDRESS | sed 's/unix\:abstract\=.*\(,guid\=.*\)/\1/' } function keepForwardingDBusToTCPSocket { while ! $KDEV_BASEDIR/kdev_dbus_socket_transformer $DBUS_FORWARDING_TCP_LOCAL_PORT --bind-only; do if (($DBUS_FORWARDING_TCP_LOCAL_PORT<$DBUS_FORWARDING_TCP_MAX_LOCAL_PORT)); then export DBUS_FORWARDING_TCP_LOCAL_PORT=$(($DBUS_FORWARDING_TCP_LOCAL_PORT+1)) # echo "Increased local port to " $DBUS_FORWARDING_TCP_LOCAL_PORT; else echo "Failed to allocate a local TCP port"; return 1; fi done $KDEV_BASEDIR/kdev_dbus_socket_transformer $DBUS_FORWARDING_TCP_LOCAL_PORT& return 0; } function keepForwardingDBusFromTCPSocket { while ! $KDEV_BASEDIR/kdev_dbus_socket_transformer $FORWARD_DBUS_FROM_PORT ${DBUS_ABSTRACT_SOCKET_TARGET_BASE_PATH}-${DBUS_ABSTRACT_SOCKET_TARGET_INDEX} --bind-only; do if ((${DBUS_ABSTRACT_SOCKET_TARGET_INDEX}<${DBUS_ABSTRACT_SOCKET_MAX_TARGET_INDEX})); then export DBUS_ABSTRACT_SOCKET_TARGET_INDEX=$(($DBUS_ABSTRACT_SOCKET_TARGET_INDEX+1)) else echo "Failed to allocate a local path for the abstract dbus socket"; return 1; fi done local PATH=${DBUS_ABSTRACT_SOCKET_TARGET_BASE_PATH}-${DBUS_ABSTRACT_SOCKET_TARGET_INDEX} export DBUS_SESSION_BUS_ADDRESS=unix:abstract=$PATH${DBUS_SOCKET_SUFFIX} $KDEV_BASEDIR/kdev_dbus_socket_transformer $FORWARD_DBUS_FROM_PORT $PATH& } function ssh! { keepForwardingDBusToTCPSocket # Start the dbus forwarding subprocess DBUS_FORWARDING_TCP_TARGET_PORT=$((5000+($RANDOM%50000))) ssh $@ -t -R localhost:$DBUS_FORWARDING_TCP_TARGET_PORT:localhost:$DBUS_FORWARDING_TCP_LOCAL_PORT \ " APPLICATION=$APPLICATION \ KDEV_BASEDIR=$KDEV_BASEDIR \ KDEV_DBUS_ID=$KDEV_DBUS_ID \ FORWARD_DBUS_FROM_PORT=$DBUS_FORWARDING_TCP_TARGET_PORT \ APPLICATION_HOST=$APPLICATION_HOST \ KDEV_WORKING_DIR=$KDEV_WORKING_DIR \ KDEV_SHELL_ENVIRONMENT_ID=$KDEV_SHELL_ENVIRONMENT_ID \ DBUS_SOCKET_SUFFIX=$(getDBusAbstractSocketSuffix) \ $(getSSHForwardOptionsFromCommand "$@") \ bash --init-file \ \$(if [ -e \"$KDEV_BASEDIR/kdevplatform_shell_environment.sh\" ]; \ then echo \"$KDEV_BASEDIR/kdevplatform_shell_environment.sh\"; \ elif [ -e \"$(which kdevplatform_shell_environment.sh)\" ]; then echo \"$(which kdevplatform_shell_environment.sh)\"; \ else \ echo \"~/.kdevplatform_shell_environment.sh\"; \ fi) \ -i" if [ "$FORWARD_DBUS_FROM_PORT" ]; then # We created the 2nd subprocess kill %2 # Stop the dbus forwarding subprocess else # We created the 1st subprocess kill %1 # Stop the dbus forwarding subprocess fi } # A version of ssh! that preserves the current working directory function ssw! { KDEV_WORKING_DIR=$(pwd) ssh! $@ } function env! { FILES="$(executeInAppSync "ls $(getSessionDir)/*.sh" "")" for FILE in $FILES; do FILE=$(basename $FILE) ID=${FILE%.sh} # This ugly construct strips away the .sh suffix if [[ "$ID" == "$KDEV_SHELL_ENVIRONMENT_ID" ]]; then echo "$ID [current]" else echo "$ID" fi done } function editenv! { local ENV_ID=$KDEV_SHELL_ENVIRONMENT_ID if [ "$1" ]; then ENV_ID=$1 fi # If the environment-file doesn't exist yet, create it executeInAppSync "if ! [ -e $(getCurrentShellEnvPath $ENV_ID) ]; then touch $(getCurrentShellEnvPath $ENV_ID); fi" "" # Open it openDocument "$(getCurrentShellEnvPath $ENV_ID)" } function setenv! { if [ "$1" ]; then KDEV_SHELL_ENVIRONMENT_ID=$1 fi # Execute the contents of the shell-environment # note: keep compatible with FreeBSD: https://bugs.kde.org/show_bug.cgi?id=311186 local TEMP=$(mktemp /tmp/$USER-XXXXXXXX) RESULT=$(executeInAppSync "cat \"$(getCurrentShellEnvPath)\"" "") echo "$RESULT" >| $TEMP if ! [ "$RESULT" ]; then # If the environment shell file doesn't exist, create it executeInAppSync "if ! [ -e $(getCurrentShellEnvPath) ]; then touch $(getCurrentShellEnvPath); fi" "" fi source $TEMP rm -f $TEMP } function showenv! { local ENV_ID=$KDEV_SHELL_ENVIRONMENT_ID if [ "$1" ]; then ENV_ID=$1 fi echo "Environment $ENV_ID:" # Execute the contents of the shell-environment echo $(executeInAppSync "cat \"$(getCurrentShellEnvPath $ENV_ID)\"" "") } if [ "$FORWARD_DBUS_FROM_PORT" ]; then # Start the target-side dbus forwarding, transforming from the ssh pipe to the abstract unix domain socket export DBUS_SESSION_BUS_ADDRESS=unix:abstract=${DBUS_ABSTRACT_SOCKET_TARGET_BASE_PATH}-${DBUS_ABSTRACT_SOCKET_TARGET_INDEX}${DBUS_SOCKET_SUFFIX} keepForwardingDBusFromTCPSocket fi setenv! ##### INITIALIZATION -------------------------------------------------------------------------------------------------------------------- # Mark that this session is attached, by prepending a '!' character PS1="!$PS1" echo "You are controlling the $APPLICATION session '$(getSessionName)'. Type help! for more information." if [ "$KDEV_WORKING_DIR" ]; then cd $KDEV_WORKING_DIR fi diff --git a/plugins/clang/clangsupport.cpp b/plugins/clang/clangsupport.cpp index a3773c1426..646486e7cf 100644 --- a/plugins/clang/clangsupport.cpp +++ b/plugins/clang/clangsupport.cpp @@ -1,440 +1,439 @@ /* This file is part of KDevelop Copyright 2013 Olivier de Gaalon Copyright 2013 Milian Wolff Copyright 2014 Kevin Funk This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "clangsupport.h" #include "clangparsejob.h" #include "util/clangdebug.h" #include "util/clangtypes.h" #include "util/clangutils.h" #include "codecompletion/model.h" #include "clanghighlighting.h" #include #include #include #include #include "codegen/clangrefactoring.h" #include "codegen/clangclasshelper.h" #include "codegen/adaptsignatureassistant.h" #include "duchain/documentfinderhelpers.h" #include "duchain/clangindex.h" #include "duchain/navigationwidget.h" #include "duchain/macrodefinition.h" #include "duchain/clangparsingenvironmentfile.h" #include "duchain/duchainutils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "clangsettings/sessionsettings/sessionsettings.h" #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(KDevClangSupportFactory, "kdevclangsupport.json", registerPlugin(); ) using namespace KDevelop; namespace { QPair lineInDocument(const QUrl &url, const KTextEditor::Cursor& position) { KDevelop::IDocument* doc = ICore::self()->documentController()->documentForUrl(url); if (!doc || !doc->textDocument() || !ICore::self()->documentController()->activeTextDocumentView()) { return {}; } const int lineNumber = position.line(); const int lineLength = doc->textDocument()->lineLength(lineNumber); KTextEditor::Range range(lineNumber, 0, lineNumber, lineLength); QString line = doc->textDocument()->text(range); return {line, range}; } QPair importedContextForPosition(const QUrl &url, const KTextEditor::Cursor& position) { auto pair = lineInDocument(url, position); const QString line = pair.first; if (line.isEmpty()) return {{}, KTextEditor::Range::invalid()}; KTextEditor::Range wordRange = ClangUtils::rangeForIncludePathSpec(line, pair.second); if (!wordRange.isValid() || !wordRange.contains(position)) { return {{}, KTextEditor::Range::invalid()}; } // Since this is called by the editor while editing, use a fast timeout so the editor stays responsive DUChainReadLocker lock(nullptr, 100); if (!lock.locked()) { clangDebug() << "Failed to lock the du-chain in time"; return {TopDUContextPointer(), KTextEditor::Range::invalid()}; } TopDUContext* topContext = DUChainUtils::standardContextForUrl(url); if (line.isEmpty() || !topContext || !topContext->parsingEnvironmentFile()) { return {TopDUContextPointer(), KTextEditor::Range::invalid()}; } // It's an #include, find out which file was included at the given line foreach(const DUContext::Import &imported, topContext->importedParentContexts()) { auto context = imported.context(nullptr); if (context) { if(topContext->transformFromLocalRevision(topContext->importPosition(context)).line() == wordRange.start().line()) { if (auto importedTop = dynamic_cast(context)) return {TopDUContextPointer(importedTop), wordRange}; } } } // The last resort. Check if the file is already included (maybe recursively from another files). // This is needed as clang doesn't visit (clang_getInclusions) those inclusions. // TODO: Maybe create an assistant that'll report whether the file is already included? auto includeName = line.mid(wordRange.start().column(), wordRange.end().column() - wordRange.start().column()); if (!includeName.isEmpty()) { if (includeName.startsWith(QLatin1Char('.'))) { const Path dir = Path(url).parent(); includeName = Path(dir, includeName).toLocalFile(); } const auto recursiveImports = topContext->recursiveImportIndices(); auto iterator = recursiveImports.iterator(); while (iterator) { const auto str = (*iterator).url().str(); if (str == includeName || (str.endsWith(includeName) && str[str.size()-includeName.size()-1] == QLatin1Char('/'))) { return {TopDUContextPointer((*iterator).data()), wordRange}; } ++iterator; } } return {{}, KTextEditor::Range::invalid()}; } QPair macroExpansionForPosition(const QUrl &url, const KTextEditor::Cursor& position) { TopDUContext* topContext = DUChainUtils::standardContextForUrl(url); if (topContext) { int useAt = topContext->findUseAt(topContext->transformToLocalRevision(position)); if (useAt >= 0) { Use use = topContext->uses()[useAt]; if (dynamic_cast(use.usedDeclaration(topContext))) { return {TopDUContextPointer(topContext), use}; } } } return {{}, Use()}; } } ClangSupport::ClangSupport(QObject* parent, const QVariantList& ) : IPlugin( QStringLiteral("kdevclangsupport"), parent ) , ILanguageSupport() , m_highlighting(nullptr) , m_refactoring(nullptr) , m_index(nullptr) { { const auto builtinDir = ClangIntegration::DUChainUtils::clangBuiltinIncludePath(); const auto headerToCheck = QLatin1String("cpuid.h"); if (!QFile::exists(builtinDir + QLatin1Char('/') + headerToCheck)) { setErrorDescription(i18n("The clang builtin include path \"%1\" is invalid (missing %2 header).\n" "Try setting the KDEV_CLANG_BUILTIN_DIR environment variable manually to fix this.\n" "See also: https://bugs.kde.org/show_bug.cgi?id=393779", builtinDir, headerToCheck)); return; } } setXMLFile( QStringLiteral("kdevclangsupport.rc") ); ClangIntegration::DUChainUtils::registerDUChainItems(); m_highlighting = new ClangHighlighting(this); m_refactoring = new ClangRefactoring(this); m_index.reset(new ClangIndex); auto model = new KDevelop::CodeCompletion( this, new ClangCodeCompletionModel(m_index.data(), this), name() ); - // TODO: use direct signal/slot connect syntax for 5.1 connect(model, &CodeCompletion::registeredToView, this, &ClangSupport::disableKeywordCompletion); connect(model, &CodeCompletion::unregisteredFromView, this, &ClangSupport::enableKeywordCompletion); const auto& mimeTypes = DocumentFinderHelpers::mimeTypesList(); for (const auto& type : mimeTypes) { KDevelop::IBuddyDocumentFinder::addFinder(type, this); } auto assistantsManager = core()->languageController()->staticAssistantsManager(); assistantsManager->registerAssistant(StaticAssistant::Ptr(new RenameAssistant(this))); assistantsManager->registerAssistant(StaticAssistant::Ptr(new AdaptSignatureAssistant(this))); connect(ICore::self()->documentController(), &IDocumentController::documentActivated, this, &ClangSupport::documentActivated); } ClangSupport::~ClangSupport() { parseLock()->lockForWrite(); // By locking the parse-mutexes, we make sure that parse jobs get a chance to finish in a good state parseLock()->unlock(); const auto& mimeTypes = DocumentFinderHelpers::mimeTypesList(); for (const auto& type : mimeTypes) { KDevelop::IBuddyDocumentFinder::removeFinder(type); } ClangIntegration::DUChainUtils::unregisterDUChainItems(); } KDevelop::ConfigPage* ClangSupport::configPage(int number, QWidget* parent) { return number == 0 ? new SessionSettings(parent) : nullptr; } int ClangSupport::configPages() const { return 1; } ParseJob* ClangSupport::createParseJob(const IndexedString& url) { return new ClangParseJob(url, this); } QString ClangSupport::name() const { return QStringLiteral("clang"); } ICodeHighlighting* ClangSupport::codeHighlighting() const { return m_highlighting; } BasicRefactoring* ClangSupport::refactoring() const { return m_refactoring; } ICreateClassHelper* ClangSupport::createClassHelper() const { return new ClangClassHelper; } ClangIndex* ClangSupport::index() { return m_index.data(); } bool ClangSupport::areBuddies(const QUrl &url1, const QUrl& url2) { return DocumentFinderHelpers::areBuddies(url1, url2); } bool ClangSupport::buddyOrder(const QUrl &url1, const QUrl& url2) { return DocumentFinderHelpers::buddyOrder(url1, url2); } QVector ClangSupport::potentialBuddies(const QUrl& url) const { return DocumentFinderHelpers::potentialBuddies(url); } void ClangSupport::createActionsForMainWindow (Sublime::MainWindow* /*window*/, QString& _xmlFile, KActionCollection& actions) { _xmlFile = xmlFile(); QAction* renameDeclarationAction = actions.addAction(QStringLiteral("code_rename_declaration")); renameDeclarationAction->setText( i18n("Rename Declaration") ); renameDeclarationAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename"))); actions.setDefaultShortcut(renameDeclarationAction, Qt::CTRL | Qt::SHIFT | Qt::Key_R); connect(renameDeclarationAction, &QAction::triggered, m_refactoring, &ClangRefactoring::executeRenameAction); QAction* moveIntoSourceAction = actions.addAction(QStringLiteral("code_move_definition")); moveIntoSourceAction->setText(i18n("Move into Source")); actions.setDefaultShortcut(moveIntoSourceAction, Qt::CTRL | Qt::ALT | Qt::Key_S); connect(moveIntoSourceAction, &QAction::triggered, m_refactoring, &ClangRefactoring::executeMoveIntoSourceAction); } KDevelop::ContextMenuExtension ClangSupport::contextMenuExtension(KDevelop::Context* context, QWidget* parent) { ContextMenuExtension cm; EditorContext *ec = dynamic_cast(context); if (ec && ICore::self()->languageController()->languagesForUrl(ec->url()).contains(this)) { // It's a C++ file, let's add our context menu. m_refactoring->fillContextMenu(cm, context, parent); } return cm; } KTextEditor::Range ClangSupport::specialLanguageObjectRange(const QUrl &url, const KTextEditor::Cursor& position) { DUChainReadLocker lock; const QPair macroExpansion = macroExpansionForPosition(url, position); if (macroExpansion.first) { return macroExpansion.first->transformFromLocalRevision(macroExpansion.second.m_range); } const QPair import = importedContextForPosition(url, position); if(import.first) { return import.second; } return KTextEditor::Range::invalid(); } QPair ClangSupport::specialLanguageObjectJumpCursor(const QUrl &url, const KTextEditor::Cursor& position) { const QPair import = importedContextForPosition(url, position); DUChainReadLocker lock; if (import.first) { return qMakePair(import.first->url().toUrl(), KTextEditor::Cursor(0,0)); } return {{}, KTextEditor::Cursor::invalid()}; } QWidget* ClangSupport::specialLanguageObjectNavigationWidget(const QUrl &url, const KTextEditor::Cursor& position) { DUChainReadLocker lock; const QPair macroExpansion = macroExpansionForPosition(url, position); if (macroExpansion.first) { Declaration* declaration = macroExpansion.second.usedDeclaration(macroExpansion.first.data()); const MacroDefinition::Ptr macroDefinition(dynamic_cast(declaration)); Q_ASSERT(macroDefinition); auto rangeInRevision = macroExpansion.first->transformFromLocalRevision(macroExpansion.second.m_range.start); return new ClangNavigationWidget(macroDefinition, DocumentCursor(IndexedString(url), rangeInRevision)); } const QPair import = importedContextForPosition(url, position); if (import.first) { return import.first->createNavigationWidget(); } return nullptr; } TopDUContext* ClangSupport::standardContext(const QUrl &url, bool /*proxyContext*/) { ClangParsingEnvironment env; return DUChain::self()->chainForDocument(url, &env); } void ClangSupport::documentActivated(IDocument* doc) { TopDUContext::Features features; { DUChainReadLocker lock; auto ctx = DUChainUtils::standardContextForUrl(doc->url()); if (!ctx) { return; } auto file = ctx->parsingEnvironmentFile(); if (!file) { return; } if (file->type() != CppParsingEnvironment) { return; } if (file->needsUpdate()) { return; } features = ctx->features(); } const auto indexedUrl = IndexedString(doc->url()); auto sessionData = ClangIntegration::DUChainUtils::findParseSessionData(indexedUrl, index()->translationUnitForUrl(IndexedString(doc->url()))); if (sessionData) { return; } if ((features & TopDUContext::AllDeclarationsContextsAndUses) != TopDUContext::AllDeclarationsContextsAndUses) { // the file was parsed in simplified mode, we need to reparse it to get all data // now that its opened in the editor features = TopDUContext::AllDeclarationsContextsAndUses; } else { features = static_cast(ClangParseJob::AttachASTWithoutUpdating | features); if (ICore::self()->languageController()->backgroundParser()->isQueued(indexedUrl)) { // The document is already scheduled for parsing (happens when opening a project with an active document) // The background parser will optimize the previous request out, so we need to update highlighting features = static_cast(ClangParseJob::UpdateHighlighting | features); } } ICore::self()->languageController()->backgroundParser()->addDocument(indexedUrl, features); } static void setKeywordCompletion(KTextEditor::View* view, bool enabled) { if (auto config = qobject_cast(view)) { config->setConfigValue(QStringLiteral("keyword-completion"), enabled); } } int ClangSupport::suggestedReparseDelayForChange(KTextEditor::Document* /*doc*/, const KTextEditor::Range& /*changedRange*/, const QString& /*changedText*/, bool /*removal*/) const { return ILanguageSupport::DefaultDelay; } void ClangSupport::disableKeywordCompletion(KTextEditor::View* view) { setKeywordCompletion(view, false); } void ClangSupport::enableKeywordCompletion(KTextEditor::View* view) { setKeywordCompletion(view, true); } #include "clangsupport.moc" diff --git a/plugins/clang/libclang_include_path.h.cmake b/plugins/clang/libclang_include_path.h.cmake index 79fa79b2b7..cfb2d7a9b7 100644 --- a/plugins/clang/libclang_include_path.h.cmake +++ b/plugins/clang/libclang_include_path.h.cmake @@ -1,22 +1,22 @@ /* This file is part of KDevelop Copyright 2018 Milian Wolff This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#define KDEV_CLANG_BUILTIN_DIR "@CLANG_LIBRARY_DIRS@/clang/@CLANG_VERSION@/include" +#define KDEV_CLANG_BUILTIN_DIR "@CLANG_BUILTIN_DIR@" diff --git a/plugins/clang/tests/minimal_visitor.cpp b/plugins/clang/tests/minimal_visitor.cpp index 9d6233bd7e..64a6e8b7e3 100644 --- a/plugins/clang/tests/minimal_visitor.cpp +++ b/plugins/clang/tests/minimal_visitor.cpp @@ -1,62 +1,70 @@ /* * Copyright 2015 Milian Wolff * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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 . */ #include #include CXChildVisitResult visitCursor(CXCursor cursor, CXCursor /*parent*/, CXClientData /*data*/) { auto range = clang_getCursorExtent(cursor); auto start = clang_getRangeStart(range); auto end = clang_getRangeEnd(range); CXFile file; unsigned startLine, startColumn; clang_getSpellingLocation(start, &file, &startLine, &startColumn, nullptr); unsigned endLine, endColumn; clang_getSpellingLocation(end, nullptr, &endLine, &endColumn, nullptr); auto str = clang_getCursorSpelling(cursor); auto fileStr = clang_getFileName(file); auto typeStr = clang_getCursorKindSpelling(cursor.kind); printf("\"%s\" [(%u, %u), (%u, %u)] in %s | %s\n", clang_getCString(str), startLine, startColumn, endLine, endColumn, clang_getCString(fileStr), clang_getCString(typeStr)); clang_disposeString(str); clang_disposeString(fileStr); clang_disposeString(typeStr); return CXChildVisit_Recurse; } int main(int argc, char** argv) { - if (argc != 2) + auto clangVersion = clang_getClangVersion(); + printf("%s\n\n", clang_getCString(clangVersion)); + clang_disposeString(clangVersion); + + if (argc != 2) { + printf("Usage: clang-minimal-visitor file.cpp\n"); return 1; + } auto index = clang_createIndex(0, 0); CXTranslationUnit unit; clang_parseTranslationUnit2(index, argv[1], nullptr, 0, nullptr, 0, 0, &unit); auto tuCursor = clang_getTranslationUnitCursor(unit); clang_visitChildren(tuCursor, &visitCursor, nullptr); + + clang_disposeTranslationUnit(unit); return 0; } diff --git a/plugins/cmake/cmakecommandscontents.cpp b/plugins/cmake/cmakecommandscontents.cpp index f3ef641ce0..67ecf2ffcd 100644 --- a/plugins/cmake/cmakecommandscontents.cpp +++ b/plugins/cmake/cmakecommandscontents.cpp @@ -1,181 +1,181 @@ /* KDevelop CMake Support * * Copyright 2009 Aleix Pol * * 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 Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "cmakecommandscontents.h" #include #include #include #include #include #include "cmakebuilderconfig.h" #include "cmakedoc.h" #include #include static const QVector args = { QLatin1String("--help-command"), QLatin1String("--help-variable"), QLatin1String("--help-module"), QLatin1String("--help-property"), QLatin1String("--help-policy"), QString() }; static QString modules [] = { i18n("Commands"), i18n("Variables"), i18n("Modules"), i18n("Properties"), i18n("Policies") }; CMakeCommandsContents::CMakeCommandsContents(QObject* parent) : QAbstractItemModel(parent) , m_namesForType(CMakeDocumentation::EOType) { for (int i = 0; i <= CMakeDocumentation::Policy; ++i) { const QStringList params = { args[i]+QStringLiteral("-list") }; QProcess* process = new QProcess(this); process->setProperty("type", i); process->setProgram(CMakeBuilderSettings::self()->cmakeExecutable().toLocalFile()); process->setArguments(params); KDevelop::ICore::self()->runtimeController()->currentRuntime()->startProcess(process); connect(process, static_cast(&QProcess::finished), this, &CMakeCommandsContents::processOutput); } } void CMakeCommandsContents::processOutput(int code) { QProcess* process = qobject_cast(sender()); if (code!=0) { qDebug() << "failed" << process; return; } const CMakeDocumentation::Type type = CMakeDocumentation::Type(process->property("type").toInt()); QTextStream stream(process); QString line = stream.readLine(); //discard first line QMap newEntries; QVector names; while(stream.readLineInto(&line)) { newEntries[line]=type; names += line; } beginInsertRows(index(type, 0, {}), 0, names.count()-1); m_typeForName.unite(newEntries); m_namesForType[type] = names; endInsertRows(); } CMakeDocumentation::Type CMakeCommandsContents::typeFor(const QString& identifier) const { //TODO can do much better if(m_typeForName.contains(identifier)) { return m_typeForName[identifier]; } else if(m_typeForName.contains(identifier.toLower())) { return m_typeForName[identifier.toLower()]; } else if(m_typeForName.contains(identifier.toUpper())) { return m_typeForName[identifier.toUpper()]; } return CMakeDocumentation::EOType; } QString CMakeCommandsContents::descriptionForIdentifier(const QString& id, CMakeDocumentation::Type t) const { QString desc; if(args[t].size() != 0) { desc = CMake::executeProcess(CMakeBuilderSettings::self()->cmakeExecutable().toLocalFile(), { args[t], id.simplified() }); desc = desc.remove(QStringLiteral(":ref:")); const QString rst2html = QStandardPaths::findExecutable(QStringLiteral("rst2html")); if (rst2html.isEmpty()) { desc = (QLatin1String("
") + desc.toHtmlEscaped() + QLatin1String("
") + i18n("

For better cmake documentation rendering, install rst2html

") + QLatin1String("")); } else { QProcess p; - p.start(rst2html, { QStringLiteral("--no-toc-backlinks") }); + p.start(rst2html, { QStringLiteral("--no-toc-backlinks"), QStringLiteral("--quiet") }); p.write(desc.toUtf8()); p.closeWriteChannel(); p.waitForFinished(); desc = QString::fromUtf8(p.readAllStandardOutput()); } } return desc; } void CMakeCommandsContents::showItemAt(const QModelIndex& idx) const { if(idx.isValid() && int(idx.internalId())>=0) { QString desc=CMakeDoc::s_provider->descriptionForIdentifier(idx.data().toString(), (ICMakeDocumentation::Type) idx.parent().row()); CMakeDoc::Ptr doc(new CMakeDoc(idx.data().toString(), desc)); KDevelop::ICore::self()->documentationController()->showDocumentation(doc); } } QModelIndex CMakeCommandsContents::parent(const QModelIndex& child) const { if(child.isValid() && child.column()==0 && int(child.internalId())>=0) return createIndex(child.internalId(),0, -1); return QModelIndex(); } QModelIndex CMakeCommandsContents::index(int row, int column, const QModelIndex& parent) const { if(row<0 || column!=0) return QModelIndex(); if(!parent.isValid() && row==ICMakeDocumentation::EOType) return QModelIndex(); return createIndex(row,column, int(parent.isValid() ? parent.row() : -1)); } int CMakeCommandsContents::rowCount(const QModelIndex& parent) const { if(!parent.isValid()) return ICMakeDocumentation::EOType; else if(int(parent.internalId())<0) { int ss=names((ICMakeDocumentation::Type) parent.row()).size(); return ss; } return 0; } QVariant CMakeCommandsContents::data(const QModelIndex& index, int role) const { if (index.isValid()) { if(role==Qt::DisplayRole) { int internal(index.internalId()); if(internal>=0) return m_namesForType[internal].count() > index.row() ? QVariant(m_namesForType[internal].at(index.row())) : QVariant(); else return modules[index.row()]; } } return QVariant(); } int CMakeCommandsContents::columnCount(const QModelIndex& /*parent*/) const { return 1; } QVector CMakeCommandsContents::names(ICMakeDocumentation::Type t) const { return m_namesForType[t]; }