mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
[apps/code] properly display the importation source
- It sould be nameOfScript.py, not just nameOfScript, when the source is a script - Fix the source for script name variables. For instance, "from script1 import *" should display the node "script1" with the source "script1.py" - If a script has the same name as a module, the module will be imported and not the script, so do not load its variables (even if the module is not in the toolbox).
This commit is contained in:
@@ -438,11 +438,13 @@ PythonToolbox::PythonToolbox() :
|
||||
}
|
||||
|
||||
const ToolboxMessageTree * PythonToolbox::moduleChildren(const char * name, int * numberOfNodes) const {
|
||||
assert(numberOfNodes != nullptr);
|
||||
for (ToolboxMessageTree t : modulesChildren) {
|
||||
if (strcmp(I18n::translate(t.label()), name) == 0) {
|
||||
*numberOfNodes = t.numberOfChildren();
|
||||
assert(*numberOfNodes > 0);
|
||||
const int childrenCount = t.numberOfChildren();
|
||||
if (numberOfNodes != nullptr) {
|
||||
*numberOfNodes = childrenCount;
|
||||
}
|
||||
assert(childrenCount > 0);
|
||||
return static_cast<const ToolboxMessageTree *>(t.children(0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
static bool DefaultName(char buffer[], size_t bufferSize);
|
||||
static bool nameCompliant(const char * name);
|
||||
|
||||
Script(Ion::Storage::Record r) : Record(r) {}
|
||||
Script(Ion::Storage::Record r = Ion::Storage::Record()) : Record(r) {}
|
||||
bool importationStatus() const;
|
||||
void toggleImportationStatus();
|
||||
const char * scriptContent() const;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
extern "C" {
|
||||
#include "py/lexer.h"
|
||||
#include "py/nlr.h"
|
||||
#include "py/objmodule.h"
|
||||
}
|
||||
|
||||
namespace Code {
|
||||
@@ -651,7 +652,7 @@ void VariableBoxController::loadGlobalAndImportedVariablesInScriptAsImported(con
|
||||
uint structKind = (uint)MP_PARSE_NODE_STRUCT_KIND(pns);
|
||||
if (structKind == PN_funcdef || structKind == PN_expr_stmt) {
|
||||
// The script is only a single function or variable definition
|
||||
addImportStruct(pns, structKind, scriptName, textToAutocomplete, textToAutocompleteLength);
|
||||
addImportStructFromScript(pns, structKind, scriptName, textToAutocomplete, textToAutocompleteLength);
|
||||
} else if (addNodesFromImportMaybe(pns, textToAutocomplete, textToAutocompleteLength)) {
|
||||
// The script is is only an import, handled in addNodesFromImportMaybe
|
||||
} else if (structKind == PN_file_input_2) {
|
||||
@@ -666,7 +667,7 @@ void VariableBoxController::loadGlobalAndImportedVariablesInScriptAsImported(con
|
||||
mp_parse_node_struct_t *child_pns = (mp_parse_node_struct_t*)(child);
|
||||
structKind = (uint)MP_PARSE_NODE_STRUCT_KIND(child_pns);
|
||||
if (structKind == PN_funcdef || structKind == PN_expr_stmt) {
|
||||
addImportStruct(child_pns, structKind, scriptName, textToAutocomplete, textToAutocompleteLength);
|
||||
addImportStructFromScript(child_pns, structKind, scriptName, textToAutocomplete, textToAutocompleteLength);
|
||||
} else {
|
||||
addNodesFromImportMaybe(child_pns, textToAutocomplete, textToAutocompleteLength);
|
||||
}
|
||||
@@ -701,9 +702,25 @@ bool VariableBoxController::addNodesFromImportMaybe(mp_parse_node_struct_t * par
|
||||
for (size_t i = 0; i < childNodesCount; i++) {
|
||||
mp_parse_node_t child = parseNode->nodes[i];
|
||||
if (MP_PARSE_NODE_IS_LEAF(child) && MP_PARSE_NODE_LEAF_KIND(child) == MP_PARSE_NODE_ID) {
|
||||
// Parsing something like "import math"
|
||||
// Parsing something like "import xyz"
|
||||
const char * id = qstr_str(MP_PARSE_NODE_LEAF_ARG(child));
|
||||
checkAndAddNode(textToAutocomplete, textToAutocompleteLength, ScriptNode::Type::WithoutParentheses, NodeOrigin::Importation, id, -1, "math", "desc" /*TODO LEA*/);
|
||||
|
||||
/* xyz might be:
|
||||
* - a module name -> in which case we want no importation source on the
|
||||
* node. The node will not be added if it is already in the builtins.
|
||||
* - a script name -> we want to have xyz.py as the importation source
|
||||
* - a non-existing identifier -> we want no source */
|
||||
const char * sourceId = nullptr;
|
||||
if (!importationSourceIsModule(id)) {
|
||||
/* If a module and a script have the same name, the micropython
|
||||
* importation algorithm first looks for a module then for a script. We
|
||||
* should thus check that the id is not a module name before retreiving
|
||||
* a script name to put it as source.
|
||||
* TODO Should importationSourceIsModule be called in
|
||||
* importationSourceIsScript?*/
|
||||
importationSourceIsScript(id, &sourceId);
|
||||
}
|
||||
checkAndAddNode(textToAutocomplete, textToAutocompleteLength, ScriptNode::Type::WithoutParentheses, NodeOrigin::Importation, id, -1, sourceId);
|
||||
} else if (MP_PARSE_NODE_IS_STRUCT(child)) {
|
||||
// Parsing something like "from math import sin"
|
||||
addNodesFromImportMaybe((mp_parse_node_struct_t *)child, textToAutocomplete, textToAutocompleteLength);
|
||||
@@ -717,68 +734,97 @@ bool VariableBoxController::addNodesFromImportMaybe(mp_parse_node_struct_t * par
|
||||
// Fetch a script / module content if needed
|
||||
if (loadAllContent) {
|
||||
assert(childNodesCount > 0);
|
||||
mp_parse_node_t importationSource = parseNode->nodes[0];
|
||||
const char * importationSourceName = nullptr;
|
||||
if (MP_PARSE_NODE_IS_LEAF(importationSource)
|
||||
&& MP_PARSE_NODE_LEAF_KIND(importationSource) == MP_PARSE_NODE_ID)
|
||||
{
|
||||
// The importation source is "simple", for instance: from math import *
|
||||
importationSourceName = qstr_str(MP_PARSE_NODE_LEAF_ARG(importationSource));
|
||||
} else if (MP_PARSE_NODE_IS_STRUCT(importationSource)) {
|
||||
mp_parse_node_struct_t * importationSourcePNS = (mp_parse_node_struct_t *)importationSource;
|
||||
uint importationSourceStructKind = MP_PARSE_NODE_STRUCT_KIND(importationSourcePNS);
|
||||
if (importationSourceStructKind == PN_dotted_name) {
|
||||
/* The importation source is "complex", for instance:
|
||||
* from matplotlib.pyplot import *
|
||||
* FIXME The solution would be to build a single qstr for this name,
|
||||
* such as in python/src/compile.c, function do_import_name, from line
|
||||
* 1117 (found by searching PN_dotted_name).
|
||||
* We might do this later, for now the only dotted name we might want to
|
||||
* find is matplolib.pyplot, so we do a very specific search. */
|
||||
int numberOfSplitNames = MP_PARSE_NODE_STRUCT_NUM_NODES(importationSourcePNS);
|
||||
if (numberOfSplitNames != 2) {
|
||||
return true;
|
||||
const char * importationSourceName = importationSourceNameFromNode(parseNode->nodes[0]);
|
||||
int numberOfModuleChildren = 0;
|
||||
const ToolboxMessageTree * moduleChildren = nullptr;
|
||||
if (importationSourceIsModule(importationSourceName, &moduleChildren, &numberOfModuleChildren)) {
|
||||
if (moduleChildren != nullptr) {
|
||||
/* The importation source is a module that we display in the toolbox:
|
||||
* get the nodes from the toolbox
|
||||
* We skip the 3 first nodes, which are "import ...", "from ... import *"
|
||||
* and "....function". */
|
||||
constexpr int numberOfNodesToSkip = 3;
|
||||
assert(numberOfModuleChildren > numberOfNodesToSkip);
|
||||
for (int i = numberOfNodesToSkip; i < numberOfModuleChildren; i++) {
|
||||
const char * name = I18n::translate((moduleChildren + i)->label());
|
||||
checkAndAddNode(textToAutocomplete, textToAutocompleteLength, ScriptNode::Type::WithoutParentheses, NodeOrigin::Importation, name, -1, importationSourceName, I18n::translate((moduleChildren + i)->text()) /*TODO LEA text or label?*/);
|
||||
}
|
||||
const char * importationSourceSubName = qstr_str(MP_PARSE_NODE_LEAF_ARG(importationSourcePNS->nodes[0]));
|
||||
if (strcmp(importationSourceSubName, "matplotlib") != 0) { //TODO LEA once rebased
|
||||
|
||||
return true;
|
||||
}
|
||||
importationSourceSubName = qstr_str(MP_PARSE_NODE_LEAF_ARG(importationSourcePNS->nodes[1]));
|
||||
if (strcmp(importationSourceSubName, "pyplot") != 0) { //TODO LEA once rebased
|
||||
|
||||
return true;
|
||||
}
|
||||
importationSourceName = "matplotlib.pyplot"; //TODO LEA once rebased
|
||||
} else {
|
||||
assert(false); //TODO LEA can we indeed assert?
|
||||
}
|
||||
}
|
||||
int numberOfChildren = 0;
|
||||
const ToolboxMessageTree * moduleChildren = static_cast<PythonToolbox *>(App::app()->toolboxForInputEventHandler(nullptr))->moduleChildren(importationSourceName, &numberOfChildren);
|
||||
if (moduleChildren != nullptr) {
|
||||
/* If the importation source is a module, get the nodes from the toolbox
|
||||
* We skip the 3 first nodes, which are "import ...", "from ... import *"
|
||||
* and "....function". */
|
||||
constexpr int numberOfNodesToSkip = 3;
|
||||
assert(numberOfChildren > numberOfNodesToSkip);
|
||||
for (int i = numberOfNodesToSkip; i < numberOfChildren; i++) {
|
||||
const char * name = I18n::translate((moduleChildren + i)->label());
|
||||
checkAndAddNode(textToAutocomplete, textToAutocompleteLength, ScriptNode::Type::WithoutParentheses, NodeOrigin::Importation, name, -1, importationSourceName, I18n::translate((moduleChildren + i)->text()) /*TODO LEA text or label?*/);
|
||||
//TODO LEA get module variables
|
||||
}
|
||||
} else {
|
||||
// Try fetching the nodes from a script
|
||||
Script importedScript = ScriptStore::ScriptBaseNamed(importationSourceName);
|
||||
if (!importedScript.isNull()) {
|
||||
Script importedScript;
|
||||
const char * scriptFullName;
|
||||
if (importationSourceIsScript(importationSourceName, &scriptFullName, &importedScript)) {
|
||||
const char * scriptContent = importedScript.scriptContent();
|
||||
assert(scriptContent != nullptr);
|
||||
loadGlobalAndImportedVariablesInScriptAsImported(importationSourceName, scriptContent, textToAutocomplete, textToAutocompleteLength);
|
||||
loadGlobalAndImportedVariablesInScriptAsImported(scriptFullName, scriptContent, textToAutocomplete, textToAutocompleteLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const char * VariableBoxController::importationSourceNameFromNode(mp_parse_node_t & node) {
|
||||
if (MP_PARSE_NODE_IS_LEAF(node) && MP_PARSE_NODE_LEAF_KIND(node) == MP_PARSE_NODE_ID) {
|
||||
// The importation source is "simple", for instance: from math import *
|
||||
return qstr_str(MP_PARSE_NODE_LEAF_ARG(node));
|
||||
}
|
||||
if (MP_PARSE_NODE_IS_STRUCT(node)) {
|
||||
mp_parse_node_struct_t * nodePNS = (mp_parse_node_struct_t *)node;
|
||||
uint nodeStructKind = MP_PARSE_NODE_STRUCT_KIND(nodePNS);
|
||||
if (nodeStructKind != PN_dotted_name) {
|
||||
return nullptr;
|
||||
}
|
||||
/* The importation source is "complex", for instance:
|
||||
* from matplotlib.pyplot import *
|
||||
* TODO LEA
|
||||
* FIXME The solution would be to build a single qstr for this name,
|
||||
* such as in python/src/compile.c, function do_import_name, from line
|
||||
* 1117 (found by searching PN_dotted_name).
|
||||
* We might do this later, for now the only dotted name we might want to
|
||||
* find is matplolib.pyplot, so we do a very specific search. */
|
||||
int numberOfSplitNames = MP_PARSE_NODE_STRUCT_NUM_NODES(nodePNS);
|
||||
if (numberOfSplitNames != 2) {
|
||||
return nullptr;
|
||||
}
|
||||
const char * nodeSubName = qstr_str(MP_PARSE_NODE_LEAF_ARG(nodePNS->nodes[0]));
|
||||
if (strcmp(nodeSubName, qstr_str(MP_QSTR_matplotlib)) == 0) {
|
||||
nodeSubName = qstr_str(MP_PARSE_NODE_LEAF_ARG(nodePNS->nodes[1]));
|
||||
if (strcmp(nodeSubName, qstr_str(MP_QSTR_pyplot)) == 0) {
|
||||
qstr_str(MP_QSTR_matplotlib_dot_pyplot);
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool VariableBoxController::importationSourceIsModule(const char * sourceName, const ToolboxMessageTree * * moduleChildren, int * numberOfModuleChildren) {
|
||||
const ToolboxMessageTree * children = static_cast<PythonToolbox *>(App::app()->toolboxForInputEventHandler(nullptr))->moduleChildren(sourceName, numberOfModuleChildren);
|
||||
if (moduleChildren != nullptr) {
|
||||
*moduleChildren = children;
|
||||
}
|
||||
if (children != nullptr) {
|
||||
return true;
|
||||
}
|
||||
// The sourceName might be a module that is not in the toolbox
|
||||
return mp_module_get(qstr_from_str(sourceName)) != MP_OBJ_NULL;
|
||||
}
|
||||
|
||||
bool VariableBoxController::importationSourceIsScript(const char * sourceName, const char * * scriptFullName, Script * retreivedScript) {
|
||||
// Try fetching the nodes from a script
|
||||
Script importedScript = ScriptStore::ScriptBaseNamed(sourceName);
|
||||
if (importedScript.isNull()) {
|
||||
return false;
|
||||
}
|
||||
*scriptFullName = importedScript.fullName();
|
||||
if (retreivedScript != nullptr) {
|
||||
*retreivedScript = importedScript;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const char * structName(mp_parse_node_struct_t * structNode) {
|
||||
// Find the id child node, which stores the struct's name
|
||||
size_t childNodesCount = MP_PARSE_NODE_STRUCT_NUM_NODES(structNode);
|
||||
@@ -795,7 +841,7 @@ const char * structName(mp_parse_node_struct_t * structNode) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void VariableBoxController::addImportStruct(mp_parse_node_struct_t * pns, uint structKind, const char * scriptName, const char * textToAutocomplete, int textToAutocompleteLength) {
|
||||
void VariableBoxController::addImportStructFromScript(mp_parse_node_struct_t * pns, uint structKind, const char * scriptName, const char * textToAutocomplete, int textToAutocompleteLength) {
|
||||
assert(structKind == PN_funcdef || structKind == PN_expr_stmt);
|
||||
// Find the id child node, which stores the struct's name
|
||||
const char * name = structName(pns);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <apps/alternate_empty_nested_menu_controller.h>
|
||||
#include <escher/message_table_cell.h>
|
||||
#include <escher/toolbox_message_tree.h>
|
||||
#include "script_node.h"
|
||||
#include "script_node_cell.h"
|
||||
#include "script_store.h"
|
||||
@@ -91,7 +92,10 @@ private:
|
||||
void loadGlobalAndImportedVariablesInScriptAsImported(const char * scriptName, const char * scriptContent, const char * textToAutocomplete, int textToAutocompleteLength);
|
||||
// Returns true if this was an import structure
|
||||
bool addNodesFromImportMaybe(mp_parse_node_struct_t * parseNode, const char * textToAutocomplete, int textToAutocompleteLength);
|
||||
void addImportStruct(mp_parse_node_struct_t * pns, uint structKind, const char * scriptName, const char * textToAutocomplete, int textToAutocompleteLength);
|
||||
const char * importationSourceNameFromNode(mp_parse_node_t & node);
|
||||
bool importationSourceIsModule(const char * sourceName, const ToolboxMessageTree * * moduleChildren = nullptr, int * numberOfModuleChildren = nullptr);
|
||||
bool importationSourceIsScript(const char * sourceName, const char * * scriptFullName, Script * retreivedScript = nullptr);
|
||||
void addImportStructFromScript(mp_parse_node_struct_t * pns, uint structKind, const char * scriptName, const char * textToAutocomplete, int textToAutocompleteLength);
|
||||
/* Add a node if it completes the text to autocomplete and if it is not
|
||||
* already contained in the variable box. */
|
||||
void checkAndAddNode(const char * textToAutocomplete, int textToAutocompleteLength, ScriptNode::Type type, NodeOrigin origin, const char * name, int nameLength, const char * nodeSourceName = nullptr, const char * description = nullptr);
|
||||
|
||||
Reference in New Issue
Block a user