From 65d977ed934e3d4cb0304b84fb2fabd8a8837976 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Mon, 22 Feb 2021 12:09:52 +0100 Subject: [PATCH] Android: generate .project and .classpath These files are needed by the java language server to operate properly. Change-Id: Ie96a1fe32fefeea7333c7fa752557f79796a1478 Reviewed-by: Christian Stenger --- src/plugins/android/javalanguageserver.cpp | 115 ++++++++++++++++++++- src/plugins/languageclient/client.h | 6 +- 2 files changed, 117 insertions(+), 4 deletions(-) diff --git a/src/plugins/android/javalanguageserver.cpp b/src/plugins/android/javalanguageserver.cpp index f40656f6c1b..a329057ab32 100644 --- a/src/plugins/android/javalanguageserver.cpp +++ b/src/plugins/android/javalanguageserver.cpp @@ -25,11 +25,17 @@ #include "javalanguageserver.h" +#include "androidconfigurations.h" #include "androidconstants.h" +#include "androidmanager.h" #include #include #include +#include +#include +#include +#include #include #include #include @@ -41,7 +47,6 @@ using namespace Utils; constexpr char languageServerKey[] = "languageServer"; -constexpr char workspaceKey[] = "workspace"; namespace Android { namespace Internal { @@ -199,6 +204,12 @@ public: using Client::Client; void executeCommand(const LanguageServerProtocol::Command &command) override; + void setCurrentProject(ProjectExplorer::Project *project) override; + void updateProjectFiles(); + void updateTarget(ProjectExplorer::Target *target); + +private: + ProjectExplorer::Target *m_currentTarget = nullptr; }; void JLSClient::executeCommand(const LanguageServerProtocol::Command &command) @@ -217,6 +228,108 @@ void JLSClient::executeCommand(const LanguageServerProtocol::Command &command) } } +void JLSClient::setCurrentProject(ProjectExplorer::Project *project) +{ + Client::setCurrentProject(project); + QTC_ASSERT(project, return); + updateTarget(project->activeTarget()); + updateProjectFiles(); + connect(project, &ProjectExplorer::Project::activeTargetChanged, + this, &JLSClient::updateTarget); +} + +static void generateProjectFile(const FilePath &projectDir, const QString &projectName) +{ + const FilePath projectFilePath = projectDir.pathAppended(".project"); + QFile projectFile(projectFilePath.toString()); + if (projectFile.open(QFile::Truncate | QFile::WriteOnly)) { + QXmlStreamWriter writer(&projectFile); + writer.setAutoFormatting(true); + writer.writeStartDocument(); + writer.writeComment("Autogenerated by Qt Creator. " + "Changes to this file will not be taken into account."); + writer.writeStartElement("projectDescription"); + writer.writeTextElement("name", projectName); + writer.writeStartElement("natures"); + writer.writeTextElement("nature", "org.eclipse.jdt.core.javanature"); + writer.writeEndElement(); // natures + writer.writeEndElement(); // projectDescription + writer.writeEndDocument(); + projectFile.close(); + } +} + +static void generateClassPathFile(const FilePath &projectDir, + const QString &sourceDir, + const QStringList &libs) +{ + const FilePath classPathFilePath = projectDir.pathAppended(".classpath"); + QFile classPathFile(classPathFilePath.toString()); + if (classPathFile.open(QFile::Truncate | QFile::WriteOnly)) { + QXmlStreamWriter writer(&classPathFile); + writer.setAutoFormatting(true); + writer.writeStartDocument(); + writer.writeComment("Autogenerated by Qt Creator. " + "Changes to this file will not be taken into account."); + writer.writeStartElement("classpath"); + writer.writeEmptyElement("classpathentry"); + writer.writeAttribute("kind", "src"); + writer.writeAttribute("path", sourceDir); + for (const QString &lib : libs) { + writer.writeEmptyElement("classpathentry"); + writer.writeAttribute("kind", "lib"); + writer.writeAttribute("path", lib); + } + writer.writeEndElement(); // classpath + writer.writeEndDocument(); + classPathFile.close(); + } +} + +void JLSClient::updateProjectFiles() +{ + using namespace ProjectExplorer; + if (!m_currentTarget) + return; + if (Target *target = m_currentTarget) { + Kit *kit = m_currentTarget->kit(); + if (DeviceTypeKitAspect::deviceTypeId(kit) != Android::Constants::ANDROID_DEVICE_TYPE) + return; + if (ProjectNode *node = project()->findNodeForBuildKey(target->activeBuildKey())) { + const FilePath &projectDir = project()->rootProjectDirectory(); + if (!projectDir.exists()) + return; + FilePath sourceDir = FilePath::fromVariant( + node->data(Constants::AndroidPackageSourceDir)); + sourceDir = sourceDir.pathAppended("src"); + if (!sourceDir.exists()) + return; + sourceDir = sourceDir.relativeChildPath(projectDir); + const FilePath &sdkLocation = AndroidConfigurations::currentConfig().sdkLocation(); + const QString &targetSDK = AndroidManager::buildTargetSDK(m_currentTarget); + const QString androidJar = QString("%1/platforms/%2/android.jar") + .arg(sdkLocation.toString(), targetSDK); + const QStringList libs(androidJar); + generateClassPathFile(projectDir, sourceDir.toString(), libs); + generateProjectFile(projectDir, project()->displayName()); + } + } +} + +void JLSClient::updateTarget(ProjectExplorer::Target *target) +{ + if (m_currentTarget) { + disconnect(m_currentTarget, &ProjectExplorer::Target::parsingFinished, + this, &JLSClient::updateProjectFiles); + } + m_currentTarget = target; + if (m_currentTarget) { + connect(m_currentTarget, &ProjectExplorer::Target::parsingFinished, + this, &JLSClient::updateProjectFiles); + } + updateProjectFiles(); +} + LanguageClient::Client *JLSSettings::createClient(LanguageClient::BaseClientInterface *interface) const { return new JLSClient(interface); diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h index 0065779e3f3..420f33a45cd 100644 --- a/src/plugins/languageclient/client.h +++ b/src/plugins/languageclient/client.h @@ -134,10 +134,10 @@ public: bool documentUpdatePostponed(const Utils::FilePath &fileName) const; // workspace control - void setCurrentProject(ProjectExplorer::Project *project); + virtual void setCurrentProject(ProjectExplorer::Project *project); const ProjectExplorer::Project *project() const; - void projectOpened(ProjectExplorer::Project *project); - void projectClosed(ProjectExplorer::Project *project); + virtual void projectOpened(ProjectExplorer::Project *project); + virtual void projectClosed(ProjectExplorer::Project *project); // commands void requestCodeActions(const LanguageServerProtocol::DocumentUri &uri,