Coco: long live the cocoplugin

Change-Id: I1d21c67233d302a0689c4689bf9c2f349387ba1c
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
David Schulz
2022-04-01 07:35:08 +02:00
parent 56b5e1f4de
commit b50eb35a17
13 changed files with 570 additions and 2 deletions

View File

@@ -61,6 +61,7 @@ add_subdirectory(languageclient)
# Level 6: # Level 6:
add_subdirectory(cmakeprojectmanager) add_subdirectory(cmakeprojectmanager)
add_subdirectory(debugger) add_subdirectory(debugger)
add_subdirectory(coco)
# Level 7: # Level 7:
add_subdirectory(android) add_subdirectory(android)

View File

@@ -0,0 +1,7 @@
add_qtc_plugin(Coco
PUBLIC_DEPENDS app_version
PLUGIN_DEPENDS Core LanguageClient
SOURCES
cocoplugin.cpp cocoplugin.h
cocolanguageclient.cpp cocolanguageclient.h
)

View File

@@ -0,0 +1,19 @@
{
\"Name\" : \"Coco\",
\"Version\" : \"$$QTCREATOR_VERSION\",
\"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
\"Experimental\" : true,
\"Vendor\" : \"The Qt Company Ltd\",
\"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\",
\"License\" : [ \"Commercial Usage\",
\"\",
\"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.\",
\"\",
\"GNU General Public License Usage\",
\"\",
\"Alternatively, this plugin may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this plugin. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html.\"
],
\"Description\" : \"Squish Coco support. Squish Coco is a code coverage tool for Tcl, QML, C# and C/C++.\",
\"Url\" : \"http://www.qt.io\",
$$dependencyList
}

21
src/plugins/coco/coco.qbs Normal file
View File

@@ -0,0 +1,21 @@
import qbs 1.0
QtcPlugin {
name: "Coco"
Depends { name: "Core" }
Depends { name: "LanguageClient" }
Depends { name: "TextEditor" }
Depends { name: "app_version_header" }
Depends { name: "Qt"; submodules: ["widgets"] }
files: [
"cocoplugin.cpp",
"cocoplugin.h",
"cocolanguageclient.cpp",
"cocolanguageclient.h",
]
}

View File

@@ -0,0 +1,228 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "cocolanguageclient.h"
#include <app/app_version.h>
#include <coreplugin/editormanager/editormanager.h>
#include <languageclient/languageclientinterface.h>
#include <projectexplorer/project.h>
#include <texteditor/texteditor.h>
#include <texteditor/texteditorsettings.h>
#include <texteditor/textmark.h>
#include <utils/utilsicons.h>
#include <QTextEdit>
using namespace LanguageClient;
using namespace LanguageServerProtocol;
using namespace Utils;
using namespace Core;
namespace Coco {
CocoLanguageClient::CocoLanguageClient(const FilePath &coco, const FilePath &csmes)
: Client(clientInterface(coco, csmes))
{
setName("Coco");
setActivateDocumentAutomatically(false);
LanguageFilter allFiles;
allFiles.filePattern = QStringList{"*"};
setSupportedLanguage(allFiles);
connect(EditorManager::instance(),
&EditorManager::documentOpened,
this,
&CocoLanguageClient::handleDocumentOpened);
connect(EditorManager::instance(),
&EditorManager::documentClosed,
this,
&CocoLanguageClient::handleDocumentClosed);
for (IDocument *openDocument : DocumentModel::openedDocuments())
handleDocumentOpened(openDocument);
ClientInfo info;
info.setName("CocoQtCreator");
info.setVersion(Core::Constants::IDE_VERSION_DISPLAY);
setClientInfo(info);
initClientCapabilities();
}
BaseClientInterface *CocoLanguageClient::clientInterface(const FilePath &coco,
const FilePath &csmes)
{
auto interface = new StdIOClientInterface();
interface->setCommandLine(CommandLine(coco, {"--lsp-stdio", "--debug", csmes.toUserOutput()}));
return interface;
}
enum class CocoDiagnosticSeverity {
Error = 1,
Warning = 2,
Information = 3,
Hint = 4,
CodeAdded = 100,
PartiallyCovered = 101,
NotCovered = 102,
FullyCovered = 103,
ManuallyValidated = 104,
DeadCode = 105,
ExecutionCountTooLow = 106,
NotCoveredInfo = 107,
CoveredInfo = 108,
ManuallyValidatedInfo = 109
};
static TextEditor::TextStyle styleForSeverity(const CocoDiagnosticSeverity &severity)
{
using namespace TextEditor;
switch (severity) {
case CocoDiagnosticSeverity::Error: return C_ERROR;
case CocoDiagnosticSeverity::Warning: return C_WARNING;
case CocoDiagnosticSeverity::Information: return C_WARNING;
case CocoDiagnosticSeverity::Hint: return C_WARNING;
case CocoDiagnosticSeverity::CodeAdded: return C_COCO_CODE_ADDED;
case CocoDiagnosticSeverity::PartiallyCovered: return C_COCO_PARTIALLY_COVERED;
case CocoDiagnosticSeverity::NotCovered: return C_COCO_NOT_COVERED;
case CocoDiagnosticSeverity::FullyCovered: return C_COCO_FULLY_COVERED;
case CocoDiagnosticSeverity::ManuallyValidated: return C_COCO_MANUALLY_VALIDATED;
case CocoDiagnosticSeverity::DeadCode: return C_COCO_DEAD_CODE;
case CocoDiagnosticSeverity::ExecutionCountTooLow: return C_COCO_EXECUTION_COUNT_TOO_LOW;
case CocoDiagnosticSeverity::NotCoveredInfo: return C_COCO_NOT_COVERED_INFO;
case CocoDiagnosticSeverity::CoveredInfo: return C_COCO_COVERED_INFO;
case CocoDiagnosticSeverity::ManuallyValidatedInfo: return C_COCO_MANUALLY_VALIDATED_INFO;
}
return C_TEXT;
}
class CocoDiagnostic : public Diagnostic
{
public:
using Diagnostic::Diagnostic;
optional<CocoDiagnosticSeverity> cocoSeverity() const
{
if (auto val = optionalValue<int>(severityKey))
return Utils::make_optional(static_cast<CocoDiagnosticSeverity>(*val));
return Utils::nullopt;
}
};
class CocoTextMark : public TextEditor::TextMark
{
public:
CocoTextMark(const FilePath &fileName, const CocoDiagnostic &diag, const Id &clientId)
: TextEditor::TextMark(fileName, diag.range().start().line() + 1, clientId)
{
setLineAnnotation(diag.message());
setToolTip(diag.message());
}
};
class CocoDiagnosticManager : public DiagnosticManager
{
public:
CocoDiagnosticManager(Client *client)
: DiagnosticManager(client)
{
setExtraSelectionsId("CocoExtraSelections");
}
private:
TextEditor::TextMark *createTextMark(const FilePath &filePath,
const Diagnostic &diagnostic,
bool /*isProjectFile*/) const override
{
const CocoDiagnostic cocoDiagnostic(diagnostic);
if (optional<CocoDiagnosticSeverity> severity = cocoDiagnostic.cocoSeverity())
return new CocoTextMark(filePath, cocoDiagnostic, client()->id());
return nullptr;
}
QTextEdit::ExtraSelection createDiagnosticSelection(const Diagnostic &diagnostic,
QTextDocument *textDocument) const override
{
if (optional<CocoDiagnosticSeverity> severity = CocoDiagnostic(diagnostic).cocoSeverity()) {
QTextCursor cursor(textDocument);
cursor.setPosition(diagnostic.range().start().toPositionInDocument(textDocument));
cursor.setPosition(diagnostic.range().end().toPositionInDocument(textDocument),
QTextCursor::KeepAnchor);
const TextEditor::TextStyle style = styleForSeverity(*severity);
const QTextCharFormat format = TextEditor::TextEditorSettings::fontSettings()
.toTextCharFormat(style);
return QTextEdit::ExtraSelection{cursor, format};
}
return {};
}
void setDiagnostics(const DocumentUri &uri,
const QList<Diagnostic> &diagnostics,
const Utils::optional<int> &version) override
{
DiagnosticManager::setDiagnostics(uri, diagnostics, version);
showDiagnostics(uri, client()->documentVersion(uri.toFilePath()));
}
};
DiagnosticManager *CocoLanguageClient::createDiagnosticManager()
{
return new CocoDiagnosticManager(this);
}
class CocoTextDocumentCapabilities : public TextDocumentClientCapabilities
{
public:
using TextDocumentClientCapabilities::TextDocumentClientCapabilities;
void enableCodecoverageSupport()
{
JsonObject coverageSupport(QJsonObject{{"codeCoverageSupport", true}});
insert("publishDiagnostics", coverageSupport);
}
};
void CocoLanguageClient::initClientCapabilities()
{
ClientCapabilities capabilities = defaultClientCapabilities();
CocoTextDocumentCapabilities textDocumentCapabilities(
capabilities.textDocument().value_or(TextDocumentClientCapabilities()));
textDocumentCapabilities.enableCodecoverageSupport();
capabilities.setTextDocument(textDocumentCapabilities);
setClientCapabilities(capabilities);
}
void CocoLanguageClient::handleDocumentOpened(IDocument *document)
{
if (auto textDocument = qobject_cast<TextEditor::TextDocument *>(document))
openDocument(textDocument);
}
void CocoLanguageClient::handleDocumentClosed(IDocument *document)
{
if (auto textDocument = qobject_cast<TextEditor::TextDocument *>(document))
closeDocument(textDocument);
}
} // namespace Coco

View File

@@ -0,0 +1,50 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <languageclient/client.h>
namespace Coco {
class CocoLanguageClient : public LanguageClient::Client
{
public:
CocoLanguageClient(const Utils::FilePath &coco, const Utils::FilePath &csmes);
LanguageClient::BaseClientInterface *clientInterface(const Utils::FilePath &coco,
const Utils::FilePath &csmes);
protected:
LanguageClient::DiagnosticManager *createDiagnosticManager() override;
private:
void initClientCapabilities();
void handleDocumentOpened(Core::IDocument *document);
void handleDocumentClosed(Core::IDocument *document);
};
} // namespace Coco

View File

@@ -0,0 +1,125 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "cocoplugin.h"
#include "cocolanguageclient.h"
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/icore.h>
#include <debugger/analyzer/analyzerconstants.h>
#include <utils/environment.h>
#include <utils/pathchooser.h>
#include <QAction>
#include <QDialogButtonBox>
#include <QFormLayout>
using namespace Utils;
namespace Coco {
class CocoPluginPrivate
{
public:
void startCoco();
CocoLanguageClient *m_client = nullptr;
};
void CocoPluginPrivate::startCoco()
{
if (m_client)
m_client->shutdown();
m_client = nullptr;
QDialog dialog(Core::ICore::dialogParent());
dialog.setModal(true);
auto layout = new QFormLayout();
const Environment env = Environment::systemEnvironment();
FilePath squishCocoPath = FilePath::fromString(env.value("SQUISHCOCO"));
const FilePaths candidates = env.findAllInPath("coveragebrowser", {squishCocoPath});
PathChooser cocoChooser;
if (!candidates.isEmpty())
cocoChooser.setFilePath(candidates.first());
cocoChooser.setExpectedKind(PathChooser::Command);
cocoChooser.setPromptDialogTitle(CocoPlugin::tr(
"Select a Squish Coco CoverageBrowser Executable"));
cocoChooser.setHistoryCompleter("Coco.CoverageBrowser.history", true);
layout->addRow(CocoPlugin::tr("CoverageBrowser:"), &cocoChooser);
PathChooser csmesChoser;
csmesChoser.setHistoryCompleter("Coco.CSMes.history", true);
csmesChoser.setExpectedKind(PathChooser::File);
csmesChoser.setInitialBrowsePathBackup(FileUtils::homePath());
csmesChoser.setPromptDialogFilter(CocoPlugin::tr("Coco instrumentation files (*.csmes)"));
csmesChoser.setPromptDialogTitle(CocoPlugin::tr("Select a Squish Coco Instrumentation File"));
layout->addRow(CocoPlugin::tr("CSMes:"), &csmesChoser);
QDialogButtonBox buttons(QDialogButtonBox::Cancel | QDialogButtonBox::Open);
layout->addWidget(&buttons);
dialog.setLayout(layout);
QObject::connect(&buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
QObject::connect(&buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
if (dialog.exec() == QDialog::Accepted) {
const FilePath cocoPath = cocoChooser.filePath();
const FilePath csmesPath = csmesChoser.filePath();
if (cocoPath.isExecutableFile() && csmesPath.exists()) {
m_client = new CocoLanguageClient(cocoPath, csmesPath);
m_client->start();
}
}
}
CocoPlugin::CocoPlugin()
: d (new CocoPluginPrivate)
{}
CocoPlugin::~CocoPlugin()
{
delete d;
}
bool CocoPlugin::initialize(const QStringList &arguments, QString *errorString)
{
using namespace Core;
ActionContainer *menu = ActionManager::actionContainer(Debugger::Constants::M_DEBUG_ANALYZER);
auto startCoco = new QAction("Squish Coco ...", this);
Command *cmd = ActionManager::registerAction(startCoco, "Coco.startCoco");
menu->addAction(cmd, Debugger::Constants::G_ANALYZER_TOOLS);
connect(startCoco, &QAction::triggered, this, [this]() { d->startCoco(); });
return true;
}
} // namespace Coco

View File

@@ -0,0 +1,48 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <extensionsystem/iplugin.h>
namespace Coco {
class CocoPluginPrivate;
class CocoPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Coco.json")
public:
CocoPlugin();
~CocoPlugin() override;
bool initialize(const QStringList &arguments, QString *errorString) override;
private:
CocoPluginPrivate *d;
};
} // namespace Coco

View File

@@ -110,8 +110,12 @@ void DiagnosticManager::showDiagnostics(const DocumentUri &uri, int version)
const bool isProjectFile = m_client->project() const bool isProjectFile = m_client->project()
&& m_client->project()->isKnownFile(filePath); && m_client->project()->isKnownFile(filePath);
for (const Diagnostic &diagnostic : versionedDiagnostics.diagnostics) { for (const Diagnostic &diagnostic : versionedDiagnostics.diagnostics) {
extraSelections << createDiagnosticSelection(diagnostic, doc->document()); const QTextEdit::ExtraSelection selection
marks.append(createTextMark(filePath, diagnostic, isProjectFile)); = createDiagnosticSelection(diagnostic, doc->document());
if (!selection.cursor.isNull())
extraSelections << selection;
if (TextEditor::TextMark *mark = createTextMark(filePath, diagnostic, isProjectFile))
marks.append(mark);
} }
if (!marks.isEmpty()) if (!marks.isEmpty())
emit textMarkCreated(filePath); emit textMarkCreated(filePath);

View File

@@ -20,6 +20,7 @@ Project {
"clearcase/clearcase.qbs", "clearcase/clearcase.qbs",
"cmakeprojectmanager/cmakeprojectmanager.qbs", "cmakeprojectmanager/cmakeprojectmanager.qbs",
"mesonprojectmanager/mesonprojectmanager.qbs", "mesonprojectmanager/mesonprojectmanager.qbs",
"coco/coco.qbs",
"compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs", "compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs",
"conan/conan.qbs", "conan/conan.qbs",
"coreplugin/coreplugin.qbs", "coreplugin/coreplugin.qbs",

View File

@@ -117,6 +117,18 @@ const char *nameForStyle(TextStyle style)
case C_OUTPUT_ARGUMENT: return "OutputArgument"; case C_OUTPUT_ARGUMENT: return "OutputArgument";
case C_STATIC_MEMBER: return "StaticMember"; case C_STATIC_MEMBER: return "StaticMember";
case C_COCO_CODE_ADDED: return "CocoCodeAdded";
case C_COCO_PARTIALLY_COVERED: return "CocoPartiallyCovered";
case C_COCO_NOT_COVERED: return "CocoNotCovered";
case C_COCO_FULLY_COVERED: return "CocoFullyCovered";
case C_COCO_MANUALLY_VALIDATED: return "CocoManuallyValidated";
case C_COCO_DEAD_CODE: return "CocoDeadCode";
case C_COCO_EXECUTION_COUNT_TOO_LOW: return "CocoExecutionCountTooLow";
case C_COCO_NOT_COVERED_INFO: return "CocoNotCoveredInfo";
case C_COCO_COVERED_INFO: return "CocoCoveredInfo";
case C_COCO_MANUALLY_VALIDATED_INFO: return "CocoManuallyValidatedInfo";
case C_LAST_STYLE_SENTINEL: return "LastStyleSentinel"; case C_LAST_STYLE_SENTINEL: return "LastStyleSentinel";
} }
return "Unknown Style"; return "Unknown Style";

View File

@@ -117,6 +117,17 @@ enum TextStyle : quint8 {
C_OUTPUT_ARGUMENT, C_OUTPUT_ARGUMENT,
C_STATIC_MEMBER, C_STATIC_MEMBER,
C_COCO_CODE_ADDED,
C_COCO_PARTIALLY_COVERED,
C_COCO_NOT_COVERED,
C_COCO_FULLY_COVERED,
C_COCO_MANUALLY_VALIDATED,
C_COCO_DEAD_CODE,
C_COCO_EXECUTION_COUNT_TOO_LOW,
C_COCO_NOT_COVERED_INFO,
C_COCO_COVERED_INFO,
C_COCO_MANUALLY_VALIDATED_INFO,
C_LAST_STYLE_SENTINEL C_LAST_STYLE_SENTINEL
}; };

View File

@@ -368,6 +368,47 @@ FormatDescriptions TextEditorSettingsPrivate::initialFormats()
tr("Names of static fields or member functions."), tr("Names of static fields or member functions."),
FormatDescription::ShowAllControls); FormatDescription::ShowAllControls);
formatDescr.emplace_back(C_COCO_CODE_ADDED,
tr("Code Coverage Added Code"),
tr("New code that was not checked for tests."),
FormatDescription::ShowAllControls);
formatDescr.emplace_back(C_COCO_PARTIALLY_COVERED,
tr("Partially Covered Code"),
tr("Partial branch/condition coverage."),
FormatDescription::ShowAllControls);
formatDescr.emplace_back(C_COCO_NOT_COVERED,
tr("Uncovered Code"),
tr("Not covered at all."),
FormatDescription::ShowAllControls);
formatDescr.emplace_back(C_COCO_FULLY_COVERED,
tr("Fully Covered Code"),
tr("Fully covered code."),
FormatDescription::ShowAllControls);
formatDescr.emplace_back(C_COCO_MANUALLY_VALIDATED,
tr("Manually Validated Code"),
tr("User added validation."),
FormatDescription::ShowAllControls);
formatDescr.emplace_back(C_COCO_DEAD_CODE,
tr("Code Coverage Dead Code"),
tr("Unreachable code."),
FormatDescription::ShowAllControls);
formatDescr.emplace_back(C_COCO_EXECUTION_COUNT_TOO_LOW,
tr("Code Coverage Execution Count To Low"),
tr("Minimum count not reached."),
FormatDescription::ShowAllControls);
formatDescr.emplace_back(C_COCO_NOT_COVERED_INFO,
tr("Implicitly Not Covered Code"),
tr("PLACEHOLDER"),
FormatDescription::ShowAllControls);
formatDescr.emplace_back(C_COCO_COVERED_INFO,
tr("Implicitly Covered Code"),
tr("PLACEHOLDER"),
FormatDescription::ShowAllControls);
formatDescr.emplace_back(C_COCO_MANUALLY_VALIDATED_INFO,
tr("Implicit Manual Coverage Validation"),
tr("PLACEHOLDER"),
FormatDescription::ShowAllControls);
return formatDescr; return formatDescr;
} }