diff --git a/src/backends/julia/juliaserver/juliaserver.h b/src/backends/julia/juliaserver/juliaserver.h --- a/src/backends/julia/juliaserver/juliaserver.h +++ b/src/backends/julia/juliaserver/juliaserver.h @@ -21,6 +21,10 @@ #include #include +#include +#include + +#include /** * Implementation of command execution server with DBus interface for Julia @@ -69,8 +73,37 @@ */ Q_SCRIPTABLE bool getWasException() const; + /** + * Reparse internal julia module and update list of variables and functions + */ + Q_SCRIPTABLE void parseModules(); + + /** + * @return list of variables in internal Julia's module + */ + Q_SCRIPTABLE QStringList variablesList(); + + /** + * @return corresponding list of values for variables from variablesList. + */ + Q_SCRIPTABLE QStringList variableValuesList(); + + /** + * @return list of function in internal Julia's module + */ + Q_SCRIPTABLE QStringList functionsList(); + +private: + void parseJlModule(jl_module_t* module); + + QString fromJuliaString(const jl_value_t* value); private: QString m_error; //< Stores last stderr output QString m_output; //< Stores last stdout output bool m_was_exception; //< Stores indicator of exception + QStringList parsedModules; + QStringList m_variables; + QStringList m_variableValues; + QStringList m_functions; + static QStringList INTERNAL_VARIABLES; }; diff --git a/src/backends/julia/juliaserver/juliaserver.cpp b/src/backends/julia/juliaserver/juliaserver.cpp --- a/src/backends/julia/juliaserver/juliaserver.cpp +++ b/src/backends/julia/juliaserver/juliaserver.cpp @@ -28,6 +28,9 @@ #include #include +QStringList JuliaServer::INTERNAL_VARIABLES = + QStringList() << QLatin1String("__originalSTDOUT__") << QLatin1String("__originalSTDERR__"); + JuliaServer::JuliaServer(QObject *parent) : QObject(parent) { @@ -139,3 +142,91 @@ return m_was_exception; } +void JuliaServer::parseModules() +{ + parseJlModule(jl_internal_main_module); +} + +void JuliaServer::parseJlModule(jl_module_t* module) +{ + jl_function_t* jl_string_function = jl_get_function(jl_base_module, "string"); + + if (module != jl_internal_main_module) + { + const QString& moduleName = fromJuliaString(jl_call1(jl_string_function, (jl_value_t*)(module->name))); + if (parsedModules.contains(moduleName)) + return; + else + parsedModules.append(moduleName); + } + + jl_function_t* jl_names_function = jl_get_function(jl_base_module, "names"); + jl_value_t* names = jl_call1(jl_names_function, (jl_value_t*)module); + jl_value_t **data = (jl_value_t**)jl_array_data(names); + for (size_t i = 0; i < jl_array_len(names); i++) + { + bool isBindingResolved = (bool)jl_binding_resolved_p(module, (jl_sym_t*)(data[i])); + if (isBindingResolved) + { + + const QString& name = fromJuliaString(jl_call1(jl_string_function, data[i])); + jl_value_t* value = jl_get_binding_or_error(module, (jl_sym_t*)(data[i]))->value; + jl_datatype_t* datetype = (jl_datatype_t*)jl_typeof(value); + QString type = QString::fromUtf8(jl_typeof_str(value)); + // Module + if (jl_is_module(value)) + { + if (module == jl_internal_main_module && (jl_module_t*)value != jl_internal_main_module) + parseJlModule((jl_module_t*)value); + } + // Function + else if (type.startsWith(QLatin1String("#")) || type == QLatin1String("Function")) + { + if (!m_functions.contains(name)) + m_functions.append(name); + } + // Variable + else if (datetype != jl_datatype_type) // Not type + { + if (module == jl_internal_main_module && !INTERNAL_VARIABLES.contains(name)) + { + const QString& valueString = fromJuliaString(jl_call1(jl_string_function, value)); + if (m_variables.contains(name)) + { + int i = m_variables.indexOf(name); + m_variableValues[i] = valueString; + } + else + { + m_variables.append(name); + m_variableValues.append(valueString); + } + } + } + } + } +} + +QString JuliaServer::fromJuliaString(const jl_value_t* value) +{ +#if JULIA_VERSION_MINOR > 5 + return QString::fromUtf8(jl_string_ptr(value)); +#else + return QString::fromUtf8(jl_string_data(value)); +#endif +} + +QStringList JuliaServer::variablesList() +{ + return m_variables; +} + +QStringList JuliaServer::variableValuesList() +{ + return m_variableValues; +} + +QStringList JuliaServer::functionsList() +{ + return m_functions; +} diff --git a/src/backends/julia/juliasession.cpp b/src/backends/julia/juliasession.cpp --- a/src/backends/julia/juliasession.cpp +++ b/src/backends/julia/juliasession.cpp @@ -224,125 +224,43 @@ void JuliaSession::listVariables() { - QStringList ignoredVariables; - ignoredVariables // These are tech variables of juliaserver - << QLatin1String("__originalSTDOUT__") - << QLatin1String("__originalSTDERR__"); - - // Wrapping removed marker to quotes - auto rem_marker = QString::fromLatin1("\"%1\"") - .arg(JuliaVariableManagementExtension::REMOVED_VARIABLE_MARKER); - - // Clear current symbols JuliaKeywords::instance()->clearVariables(); JuliaKeywords::instance()->clearFunctions(); - QStringList processed_modules; // modules we have processed - QStringList modules_to_process; // modules in queue - modules_to_process << QLatin1String("__GLOBAL__"); // starting from global - - while (modules_to_process.size() > 0) { - // Get from queue - auto module = modules_to_process.front(); - modules_to_process.pop_front(); - if (processed_modules.contains(module)) { + m_interface->call(QLatin1String("parseModules")); + + const QStringList& variables = + static_cast>(m_interface->call(QLatin1String("variablesList"))).value(); + const QStringList& values = + static_cast>(m_interface->call(QLatin1String("variableValuesList"))).value(); + for (int i = 0; i < variables.size(); i++) + { + if (i >= values.size()) + { + qWarning() << "Don't have value for variable from julia server response, somethinkg wrong!"; continue; } - processed_modules << module; - - // Get whos() output, maybe from cache - QString whos_output; - if (module == QLatin1String("__GLOBAL__")) { - runJuliaCommand(QLatin1String("whos()")); - whos_output = getOutput(); - } else { - auto it = m_whos_cache.find(module); - if (it == m_whos_cache.end()) { - runJuliaCommand(QString::fromLatin1("whos(%1)").arg(module)); - whos_output = getOutput(); - m_whos_cache[module] = whos_output; - } else { - whos_output = it.value(); - } - } - - // In this lists we will collect symbols to apply `show` to them - // in one DBus call - QStringList batchCommands; - QStringList batchTypes; - QStringList batchNames; - for (auto line : whos_output.split(QLatin1String("\n"))) { - QString name = - line.simplified().split(QLatin1String(" ")).first().simplified(); - - if (name.isEmpty()) { // some empty line - continue; - } - - QString type = - line.simplified().split(QLatin1String(" ")).last().simplified(); -#if JULIA_VERSION_MINOR > 5 - if (line.contains(QLatin1String("#"))) - type = QLatin1String("Function"); -#endif - - if (ignoredVariables.contains(name)) { - // Ignored variable - continue; - } - - if (type == QLatin1String("Module")) { - // Found module, place in queue - modules_to_process.append(name); - continue; - } - - if (type == QLatin1String("Function")) { - // Found function - JuliaKeywords::instance()->addFunction(name); - continue; - } - - if (module != QLatin1String("__GLOBAL__")) { - continue; // Don't add variables not included on global scope - } - - // Add to batch - batchCommands << QString::fromLatin1("show(%1);").arg(name); - batchTypes << type; - batchNames << name; - } - - if (batchCommands.isEmpty()) { - continue; // nothing to do - } - - // Run batched command - runJuliaCommand( - batchCommands.join(QLatin1String("print(\"__CANTOR_DELIM__\");")) - ); - auto values = getOutput().split(QLatin1String("__CANTOR_DELIM__")); - - for (int i = 0; i < values.size(); i++) { - auto value = values[i].simplified(); - auto type = batchTypes[i]; - auto name = batchNames[i]; - - if (type == QLatin1String("ASCIIString") || type == QLatin1String("String")) { - if (value == rem_marker) { - // This is removed variable - m_variableModel->removeVariable(name); - continue; - } - } + const QString& name = variables[i]; + QString value = values[i]; + if (value != JuliaVariableManagementExtension::REMOVED_VARIABLE_MARKER) + { // Register variable // We use replace here, because julia return data type for some variables, and we need // remove it to make variable view more consistent with the other backends // More info: https://bugs.kde.org/show_bug.cgi?id=377771 m_variableModel->addVariable(name, value.replace(typeVariableInfo,QLatin1String("["))); JuliaKeywords::instance()->addVariable(name); } + else + m_variableModel->removeVariable(name); + } + + const QStringList& functions = + static_cast>(m_interface->call(QLatin1String("functionsList"))).value(); + foreach (const QString& name, functions) + { + JuliaKeywords::instance()->addFunction(name); } emit updateHighlighter();