diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index 3100b47882..1ca1ac2842 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -39,6 +40,21 @@ const QString RESULT_ERROR_STYLE = "color: #d13b22;"; const QString GUTTER_PREVIOUS_COMMAND = "<"; const QString GUTTER_ERROR = "X"; +const QString JSDOC_LINE_SEPARATOR = "\r"; + +const QString JSDOC_STYLE = + ""; + const QString JSConsole::_consoleFileName { "about:console" }; const QString JSON_KEY = "entries"; @@ -50,7 +66,7 @@ QList _readLines(const QString& filename) { // TODO: check root["version"] return root[JSON_KEY].toVariant().toStringList(); } - + void _writeLines(const QString& filename, const QList& lines) { QFile file(filename); file.open(QFile::WriteOnly); @@ -62,6 +78,10 @@ void _writeLines(const QString& filename, const QList& lines) { QTextStream(&file) << json; } +QString _jsdocTypeToString(QJsonValue jsdocType) { + return jsdocType.toObject().value("names").toVariant().toStringList().join("/"); +} + void JSConsole::readAPI() { QFile file(PathUtils::resourcesPath() + "auto-complete/hifiJSDoc.json"); file.open(QFile::ReadOnly); @@ -102,7 +122,10 @@ QStandardItemModel* JSConsole::getAutoCompleteModel(const QString& memberOf) { foreach(auto doc, _apiDocs) { auto object = doc.toObject(); auto scope = object.value("scope"); - if ((memberOfProperty == nullptr && scope.toString() == "global" && object.value("kind").toString() == "namespace") || (memberOfProperty != nullptr && object.value("memberof").toString() == memberOfProperty)) { + if ((memberOfProperty == nullptr && scope.toString() == "global" && object.value("kind").toString() == "namespace") || + (memberOfProperty != nullptr && object.value("memberof").toString() == memberOfProperty && + object.value("kind").toString() != "typedef")) { + model->appendRow(getAutoCompleteItem(doc)); } } @@ -166,20 +189,119 @@ JSConsole::JSConsole(QWidget* parent, const ScriptEnginePointer& scriptEngine) : } void JSConsole::insertCompletion(const QModelIndex& completion) { + auto jsdocObject = QJsonValue::fromVariant(completion.data(Qt::UserRole + 1)).toObject(); + auto kind = jsdocObject.value("kind").toString(); auto completionString = completion.data().toString(); - QTextCursor tc = _ui->promptTextEdit->textCursor(); + if (kind == "function") { + auto params = jsdocObject.value("params").toArray(); + // automatically add the parenthesis/parentheses for the functions + completionString += params.isEmpty() ? "()" : "("; + } + QTextCursor textCursor = _ui->promptTextEdit->textCursor(); int extra = completionString.length() - _completer->completionPrefix().length(); - tc.movePosition(QTextCursor::Left); - tc.movePosition(QTextCursor::EndOfWord); - tc.insertText(completionString.right(extra)); - _ui->promptTextEdit->setTextCursor(tc); + textCursor.movePosition(QTextCursor::Left); + textCursor.movePosition(QTextCursor::EndOfWord); + textCursor.insertText(completionString.right(extra)); + _ui->promptTextEdit->setTextCursor(textCursor); } void JSConsole::highlightedCompletion(const QModelIndex& completion) { - qDebug() << "Highlighted " << completion.data().toString(); auto jsdocObject = QJsonValue::fromVariant(completion.data(Qt::UserRole + 1)).toObject(); - - // qDebug() << "Highlighted data " << QJsonDocument(jsdocObject).toJson(QJsonDocument::Compact); + QString memberOf = ""; + if (!_completerModule.isEmpty()) { + memberOf = _completerModule + "."; + } + auto name = memberOf + "" + jsdocObject.value("name").toString() + ""; + auto description = jsdocObject.value("description").toString(); + auto examples = jsdocObject.value("examples").toArray(); + auto kind = jsdocObject.value("kind").toString(); + QString returnTypeText = ""; + + QString paramsTable = ""; + if (kind == "function") { + auto params = jsdocObject.value("params").toArray(); + auto returns = jsdocObject.value("returns"); + if (!returns.isUndefined()) { + returnTypeText = _jsdocTypeToString(jsdocObject.value("returns").toArray().at(0).toObject().value("type")) + " "; + } + name += "("; + if (!params.isEmpty()) { + bool hasDefaultParam = false; + bool hasOptionalParam = false; + bool firstItem = true; + foreach(auto param, params) { + auto paramObject = param.toObject(); + if (!hasOptionalParam && paramObject.value("optional").toBool(false)) { + hasOptionalParam = true; + name += "["; + } + if (!firstItem) { + name += ", "; + } else { + firstItem = false; + } + name += paramObject.value("name").toString(); + if (!hasDefaultParam && !paramObject.value("defaultvalue").isUndefined()) { + hasDefaultParam = true; + } + } + if (hasOptionalParam) { + name += "]"; + } + + paramsTable += ""; + if (hasDefaultParam) { + paramsTable += ""; + } + paramsTable += ""; + foreach(auto param, params) { + auto paramObject = param.toObject(); + paramsTable += ""; + } + + paramsTable += "
NameTypeDefaultDescription
" + paramObject.value("name").toString() + "" + + _jsdocTypeToString(paramObject.value("type")) + ""; + if (hasDefaultParam) { + paramsTable += paramObject.value("defaultvalue").toVariant().toString() + ""; + } + paramsTable += paramObject.value("description").toString() + "
"; + } + name += ")"; + } else if (!jsdocObject.value("type").isUndefined()){ + returnTypeText = _jsdocTypeToString(jsdocObject.value("type")) + " "; + } + auto popupText = JSDOC_STYLE + "" + returnTypeText + name + ""; + auto descriptionText = "

" + description.replace(JSDOC_LINE_SEPARATOR, "
") + "

"; + + popupText += descriptionText; + popupText += paramsTable; + auto returns = jsdocObject.value("returns"); + if (!returns.isUndefined()) { + foreach(auto returnEntry, returns.toArray()) { + auto returnsObject = returnEntry.toObject(); + auto returnsDescription = returnsObject.value("description").toString().replace(JSDOC_LINE_SEPARATOR, "
"); + popupText += "

Returns

" + returnsDescription + "

Type
" +
+                _jsdocTypeToString(returnsObject.value("type")) + "
"; + } + } + + if (!examples.isEmpty()) { + popupText += "

Examples

"; + foreach(auto example, examples) { + auto exampleText = example.toString(); + auto exampleLines = exampleText.split(JSDOC_LINE_SEPARATOR); + foreach(auto exampleLine, exampleLines) { + if (exampleLine.contains("")) { + popupText += exampleLine.replace("caption>", "h5>"); + } else { + popupText += "
" + exampleLine + "\n
"; + } + } + } + } + + QToolTip::showText(QPoint(_completer->popup()->pos().x() + _completer->popup()->width(), _completer->popup()->pos().y()), + popupText, _completer->popup()); } JSConsole::~JSConsole() { @@ -299,7 +421,7 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) { case Qt::Key_Space: case Qt::Key_Enter: case Qt::Key_Return: - insertCompletion(_completer->currentIndex()); + insertCompletion(_completer->popup()->currentIndex()); _completer->popup()->hide(); return true; default: @@ -401,6 +523,7 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) { cursorRect.setWidth(_completer->popup()->sizeHintForColumn(0) + _completer->popup()->verticalScrollBar()->sizeHint().width()); _completer->complete(cursorRect); + highlightedCompletion(_completer->popup()->currentIndex()); return false; } }