Showing the changes to interface/src/ui/JSConsole.cpp (1 of 1 file). Show all
Unified · Split
| @@ -17,6 +17,7 @@ · 16 unmodified lines | ||
| 17 | 17 | #include <QtConcurrent/QtConcurrentRun> |
| 18 | 18 | #include <QStringListModel> |
| 19 | 19 | #include <QListView> |
| 20 | +#include <QToolTip> | |
| 20 | 21 | |
| 21 | 22 | #include <shared/QtHelpers.h> |
| 22 | 23 | #include <ScriptEngines.h> |
| @@ -39,6 +40,21 @@ const QString RESULT_ERROR_STYLE = "color: #d13b22;"; · 16 unmodified lines | ||
| 39 | 40 | const QString GUTTER_PREVIOUS_COMMAND = "<span style=\"color: #57b8bb;\"><</span>"; |
| 40 | 41 | const QString GUTTER_ERROR = "<span style=\"color: #d13b22;\">X</span>"; |
| 41 | 42 | |
| 43 | +const QString JSDOC_LINE_SEPARATOR = "\r"; | |
| 44 | + | |
| 45 | +const QString JSDOC_STYLE = | |
| 46 | + "<style type=\"text/css\"> \ | |
| 47 | + .code { \ | |
| 48 | + font-family: Consolas, Monaco, 'Andale Mono', monospace \ | |
| 49 | + } \ | |
| 50 | + pre, code { \ | |
| 51 | + display: inline; \ | |
| 52 | + } \ | |
| 53 | + .no-wrap { \ | |
| 54 | + white-space: nowrap; \ | |
| 55 | + } \ | |
| 56 | + </style>"; | |
| 57 | + | |
| 42 | 58 | const QString JSConsole::_consoleFileName { "about:console" }; |
| 43 | 59 | |
| 44 | 60 | const QString JSON_KEY = "entries"; |
| @@ -50,7 +66,7 @@ QList<QString> _readLines(const QString& filename) { · 5 unmodified lines | ||
| 50 | 66 | // TODO: check root["version"] |
| 51 | 67 | return root[JSON_KEY].toVariant().toStringList(); |
| 52 | 68 | } |
| 53 | − | |
| 69 | + | |
| 54 | 70 | void _writeLines(const QString& filename, const QList<QString>& lines) { |
| 55 | 71 | QFile file(filename); |
| 56 | 72 | file.open(QFile::WriteOnly); |
| @@ -62,6 +78,10 @@ void _writeLines(const QString& filename, const QList<QString>& lines) { · 5 unmodified lines | ||
| 62 | 78 | QTextStream(&file) << json; |
| 63 | 79 | } |
| 64 | 80 | |
| 81 | +QString _jsdocTypeToString(QJsonValue jsdocType) { | |
| 82 | + return jsdocType.toObject().value("names").toVariant().toStringList().join("/"); | |
| 83 | +} | |
| 84 | + | |
| 65 | 85 | void JSConsole::readAPI() { |
| 66 | 86 | QFile file(PathUtils::resourcesPath() + "auto-complete/hifiJSDoc.json"); |
| 67 | 87 | file.open(QFile::ReadOnly); |
| @@ -102,7 +122,10 @@ QStandardItemModel* JSConsole::getAutoCompleteModel(const QString& memberOf) { · 34 unmodified lines | ||
| 102 | 122 | foreach(auto doc, _apiDocs) { |
| 103 | 123 | auto object = doc.toObject(); |
| 104 | 124 | auto scope = object.value("scope"); |
| 105 | − if ((memberOfProperty == nullptr && scope.toString() == "global" && object.value("kind").toString() == "namespace") || (memberOfProperty != nullptr && object.value("memberof").toString() == memberOfProperty)) { | |
| 125 | + if ((memberOfProperty == nullptr && scope.toString() == "global" && object.value("kind").toString() == "namespace") || | |
| 126 | + (memberOfProperty != nullptr && object.value("memberof").toString() == memberOfProperty && | |
| 127 | + object.value("kind").toString() != "typedef")) { | |
| 128 | + | |
| 106 | 129 | model->appendRow(getAutoCompleteItem(doc)); |
| 107 | 130 | } |
| 108 | 131 | } |
| @@ -166,20 +189,119 @@ JSConsole::JSConsole(QWidget* parent, const ScriptEnginePointer& scriptEngine) : · 57 unmodified lines | ||
| 166 | 189 | } |
| 167 | 190 | |
| 168 | 191 | void JSConsole::insertCompletion(const QModelIndex& completion) { |
| 192 | + auto jsdocObject = QJsonValue::fromVariant(completion.data(Qt::UserRole + 1)).toObject(); | |
| 193 | + auto kind = jsdocObject.value("kind").toString(); | |
| 169 | 194 | auto completionString = completion.data().toString(); |
| 170 | − QTextCursor tc = _ui->promptTextEdit->textCursor(); | |
| 195 | + if (kind == "function") { | |
| 196 | + auto params = jsdocObject.value("params").toArray(); | |
| 197 | + // automatically add the parenthesis/parentheses for the functions | |
| 198 | + completionString += params.isEmpty() ? "()" : "("; | |
| 199 | + } | |
| 200 | + QTextCursor textCursor = _ui->promptTextEdit->textCursor(); | |
| 171 | 201 | int extra = completionString.length() - _completer->completionPrefix().length(); |
| 172 | − tc.movePosition(QTextCursor::Left); | |
| 173 | − tc.movePosition(QTextCursor::EndOfWord); | |
| 174 | − tc.insertText(completionString.right(extra)); | |
| 175 | − _ui->promptTextEdit->setTextCursor(tc); | |
| 202 | + textCursor.movePosition(QTextCursor::Left); | |
| 203 | + textCursor.movePosition(QTextCursor::EndOfWord); | |
| 204 | + textCursor.insertText(completionString.right(extra)); | |
| 205 | + _ui->promptTextEdit->setTextCursor(textCursor); | |
| 176 | 206 | } |
| 177 | 207 | |
| 178 | 208 | void JSConsole::highlightedCompletion(const QModelIndex& completion) { |
| 179 | − qDebug() << "Highlighted " << completion.data().toString(); | |
| 180 | 209 | auto jsdocObject = QJsonValue::fromVariant(completion.data(Qt::UserRole + 1)).toObject(); |
| 181 | − | |
| 182 | − // qDebug() << "Highlighted data " << QJsonDocument(jsdocObject).toJson(QJsonDocument::Compact); | |
| 210 | + QString memberOf = ""; | |
| 211 | + if (!_completerModule.isEmpty()) { | |
| 212 | + memberOf = _completerModule + "."; | |
| 213 | + } | |
| 214 | + auto name = memberOf + "<b>" + jsdocObject.value("name").toString() + "</b>"; | |
| 215 | + auto description = jsdocObject.value("description").toString(); | |
| 216 | + auto examples = jsdocObject.value("examples").toArray(); | |
| 217 | + auto kind = jsdocObject.value("kind").toString(); | |
| 218 | + QString returnTypeText = ""; | |
| 219 | + | |
| 220 | + QString paramsTable = ""; | |
| 221 | + if (kind == "function") { | |
| 222 | + auto params = jsdocObject.value("params").toArray(); | |
| 223 | + auto returns = jsdocObject.value("returns"); | |
| 224 | + if (!returns.isUndefined()) { | |
| 225 | + returnTypeText = _jsdocTypeToString(jsdocObject.value("returns").toArray().at(0).toObject().value("type")) + " "; | |
| 226 | + } | |
| 227 | + name += "("; | |
| 228 | + if (!params.isEmpty()) { | |
| 229 | + bool hasDefaultParam = false; | |
| 230 | + bool hasOptionalParam = false; | |
| 231 | + bool firstItem = true; | |
| 232 | + foreach(auto param, params) { | |
| 233 | + auto paramObject = param.toObject(); | |
| 234 | + if (!hasOptionalParam && paramObject.value("optional").toBool(false)) { | |
| 235 | + hasOptionalParam = true; | |
| 236 | + name += "<i>["; | |
| 237 | + } | |
| 238 | + if (!firstItem) { | |
| 239 | + name += ", "; | |
| 240 | + } else { | |
| 241 | + firstItem = false; | |
| 242 | + } | |
| 243 | + name += paramObject.value("name").toString(); | |
| 244 | + if (!hasDefaultParam && !paramObject.value("defaultvalue").isUndefined()) { | |
| 245 | + hasDefaultParam = true; | |
| 246 | + } | |
| 247 | + } | |
| 248 | + if (hasOptionalParam) { | |
| 249 | + name += "]</i>"; | |
| 250 | + } | |
| 251 | + | |
| 252 | + paramsTable += "<table border=\"1\" cellpadding=\"10\"><thead><tr><th>Name</th><th>Type</th>"; | |
| 253 | + if (hasDefaultParam) { | |
| 254 | + paramsTable += "<th>Default</th>"; | |
| 255 | + } | |
| 256 | + paramsTable += "<th>Description</th></tr></thead><tbody>"; | |
| 257 | + foreach(auto param, params) { | |
| 258 | + auto paramObject = param.toObject(); | |
| 259 | + paramsTable += "<tr><td>" + paramObject.value("name").toString() + "</td><td>" + | |
| 260 | + _jsdocTypeToString(paramObject.value("type")) + "</td><td>"; | |
| 261 | + if (hasDefaultParam) { | |
| 262 | + paramsTable += paramObject.value("defaultvalue").toVariant().toString() + "</td><td>"; | |
| 263 | + } | |
| 264 | + paramsTable += paramObject.value("description").toString() + "</td></tr>"; | |
| 265 | + } | |
| 266 | + | |
| 267 | + paramsTable += "</tbody></table>"; | |
| 268 | + } | |
| 269 | + name += ")"; | |
| 270 | + } else if (!jsdocObject.value("type").isUndefined()){ | |
| 271 | + returnTypeText = _jsdocTypeToString(jsdocObject.value("type")) + " "; | |
| 272 | + } | |
| 273 | + auto popupText = JSDOC_STYLE + "<span class=\"no-wrap\">" + returnTypeText + name + "</span>"; | |
| 274 | + auto descriptionText = "<p>" + description.replace(JSDOC_LINE_SEPARATOR, "<br>") + "</p>"; | |
| 275 | + | |
| 276 | + popupText += descriptionText; | |
| 277 | + popupText += paramsTable; | |
| 278 | + auto returns = jsdocObject.value("returns"); | |
| 279 | + if (!returns.isUndefined()) { | |
| 280 | + foreach(auto returnEntry, returns.toArray()) { | |
| 281 | + auto returnsObject = returnEntry.toObject(); | |
| 282 | + auto returnsDescription = returnsObject.value("description").toString().replace(JSDOC_LINE_SEPARATOR, "<br>"); | |
| 283 | + popupText += "<h4>Returns</h4><p>" + returnsDescription + "</p><h5>Type</h5><pre><code>" + | |
| 284 | + _jsdocTypeToString(returnsObject.value("type")) + "</code></pre>"; | |
| 285 | + } | |
| 286 | + } | |
| 287 | + | |
| 288 | + if (!examples.isEmpty()) { | |
| 289 | + popupText += "<h4>Examples</h4>"; | |
| 290 | + foreach(auto example, examples) { | |
| 291 | + auto exampleText = example.toString(); | |
| 292 | + auto exampleLines = exampleText.split(JSDOC_LINE_SEPARATOR); | |
| 293 | + foreach(auto exampleLine, exampleLines) { | |
| 294 | + if (exampleLine.contains("<caption>")) { | |
| 295 | + popupText += exampleLine.replace("caption>", "h5>"); | |
| 296 | + } else { | |
| 297 | + popupText += "<pre><code>" + exampleLine + "\n</code></pre>"; | |
| 298 | + } | |
| 299 | + } | |
| 300 | + } | |
| 301 | + } | |
| 302 | + | |
| 303 | + QToolTip::showText(QPoint(_completer->popup()->pos().x() + _completer->popup()->width(), _completer->popup()->pos().y()), | |
| 304 | + popupText, _completer->popup()); | |
| 183 | 305 | } |
| 184 | 306 | |
| 185 | 307 | JSConsole::~JSConsole() { |
| @@ -299,7 +421,7 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) { · 113 unmodified lines | ||
| 299 | 421 | case Qt::Key_Space: |
| 300 | 422 | case Qt::Key_Enter: |
| 301 | 423 | case Qt::Key_Return: |
| 302 | − insertCompletion(_completer->currentIndex()); | |
| 424 | + insertCompletion(_completer->popup()->currentIndex()); | |
| 303 | 425 | _completer->popup()->hide(); |
| 304 | 426 | return true; |
| 305 | 427 | default: |
| @@ -401,6 +523,7 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) { · 95 unmodified lines | ||
| 401 | 523 | cursorRect.setWidth(_completer->popup()->sizeHintForColumn(0) + |
| 402 | 524 | _completer->popup()->verticalScrollBar()->sizeHint().width()); |
| 403 | 525 | _completer->complete(cursorRect); |
| 526 | + highlightedCompletion(_completer->popup()->currentIndex()); | |
| 404 | 527 | return false; |
| 405 | 528 | } |
| 406 | 529 | } |