Drop-down widgets for extension functions via UnresolvedSymbol (#7115)

Fixes #6955 by:
- using `visualisationModule` to specify the module where the visualization is to be used
- referring to method in `Meta.get_annotation` with `.method_name` - e.g. unresolved symbol notation
- evaluating arguments to `Meta.get_annotation` in the context of the user module (which can access the extension functions)
This commit is contained in:
Jaroslav Tulach 2023-06-27 17:19:42 +02:00 committed by GitHub
parent 22259e696d
commit 477dd82670
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 213 additions and 109 deletions

View File

@ -434,6 +434,7 @@ fn test_execution_context() {
};
let positional_arguments_expressions = vec![1, 2, 3].iter().map(|x| x.to_string()).collect();
let visualization_config = VisualizationConfiguration {
visualization_module: "Foo.Bar.Baz".to_string(),
execution_context_id: context_id,
expression,
positional_arguments_expressions,
@ -453,6 +454,7 @@ fn test_execution_context() {
"definedOnType" : "[Foo.Bar.Baz]",
"name" : "foo"
},
"visualizationModule" : "Foo.Bar.Baz",
"positionalArgumentsExpressions" : ["1", "2", "3"]
}
}),
@ -479,6 +481,7 @@ fn test_execution_context() {
};
let positional_arguments_expressions = vec!["foo"].iter().map(|x| x.to_string()).collect();
let visualization_config = VisualizationConfiguration {
visualization_module: "Foo.Bar.Baz".to_string(),
execution_context_id: context_id,
expression,
positional_arguments_expressions,
@ -495,6 +498,7 @@ fn test_execution_context() {
"definedOnType" : "[Foo.Bar.Baz]",
"name" : "foo"
},
"visualizationModule" : "Foo.Bar.Baz",
"positionalArgumentsExpressions" : ["foo"]
}
}),

View File

@ -713,6 +713,8 @@ pub type ExpressionId = Uuid;
#[serde(rename_all = "camelCase")]
#[allow(missing_docs)]
pub struct VisualizationConfiguration {
/// Module to evaluate visualization in context of.
pub visualization_module: String,
/// An execution context of the visualization.
pub execution_context_id: ContextId,
/// An enso function that will transform the data into expected format.

View File

@ -403,10 +403,19 @@ impl Handle {
}
/// Get a fully qualified name of the module in the [`graph`]. The name is obtained from the
/// module's path and the `project` name.
pub fn module_qualified_name_with_project(
&self,
project: &dyn model::project::API,
) -> QualifiedName {
self.graph().module.path().qualified_module_name(project.qualified_name())
}
/// Get a full qualified name of the module in the [`graph`]. The name is obtained from the
/// module's path and the `project` name.
pub fn module_qualified_name(&self, project: &dyn model::project::API) -> QualifiedName {
self.graph().module.path().qualified_module_name(project.qualified_name())
pub fn module_qualified_name(&self) -> QualifiedName {
self.graph().module.path().qualified_module_name(self.project.qualified_name())
}
/// Returns information about all the connections between graph's nodes.

View File

@ -392,7 +392,7 @@ impl QueryData {
/// Generate visualization metadata for this query.
fn visualization_metadata(&self) -> Metadata {
let arguments: Vec<Code> = vec![
Self::escape_visualization_argument(&self.method_name).into(),
Self::as_unresolved_symbol(&self.method_name).into(),
Self::arg_sequence(&self.arguments).into(),
];
@ -410,6 +410,12 @@ impl QueryData {
Ast::raw_text_literal(arg).repr()
}
/// Creates unresolved symbol via ".name" syntax. Unresolved symbol contains name and also
/// module scope to resolve it properly.
fn as_unresolved_symbol(arg: &str) -> String {
format!(".{arg}")
}
/// Escape a list of strings to be used as a visualization argument. Transforms the strings into
/// an enso expression with a list of string literals.
fn arg_sequence(args: &[ImString]) -> String {

View File

@ -574,7 +574,7 @@ impl Searcher {
"Standard.Visualization.AI",
"build_ai_prompt",
)?;
let vis = Visualization::new(this.id, vis_ptr, vec![]);
let vis = Visualization::new(vis_ptr.module.to_owned(), this.id, vis_ptr, vec![]);
let mut result = graph.attach_visualization(vis.clone()).await?;
let next = result.next().await.ok_or(NoAIVisualizationDataReceived)?;
let prompt = std::str::from_utf8(&next)?;
@ -1091,7 +1091,7 @@ impl Searcher {
}
fn module_qualified_name(&self) -> QualifiedName {
self.graph.module_qualified_name(&*self.project)
self.graph.module_qualified_name_with_project(&*self.project)
}
fn filter(&self) -> Filter {

View File

@ -10,6 +10,7 @@ use crate::model::execution_context::VisualizationId;
use crate::model::execution_context::VisualizationUpdateData;
use crate::sync::Synchronized;
use double_representation::name::QualifiedName;
use futures::channel::mpsc::UnboundedReceiver;
use futures::future::ready;
use ide_view::graph_editor::component::visualization::Metadata;
@ -158,6 +159,7 @@ impl Default for Status {
#[allow(missing_docs)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Desired {
pub module: QualifiedName,
pub visualization_id: VisualizationId,
pub expression_id: ast::Id,
pub metadata: Metadata,
@ -285,11 +287,15 @@ impl Manager {
// Early return: requested to remove visualization that was already removed.
return;
};
let prj = self.executed_graph.module_qualified_name();
let graph = self.executed_graph.graph();
let module = prj.new_child(graph.module.name());
let current_id = current.as_ref().and_then(|current| current.latest_id());
let new_desired = new_desired.map(|new_desired| Desired {
expression_id: target,
module,
expression_id: target,
visualization_id: current_id.unwrap_or_else(VisualizationId::new_v4),
metadata: new_desired,
metadata: new_desired,
});
self.write_new_desired(target, new_desired)
}
@ -332,6 +338,7 @@ impl Manager {
Ok(Visualization {
id: desired.visualization_id,
expression_id: desired.expression_id,
module: desired.module,
method_pointer,
arguments,
})
@ -556,13 +563,14 @@ mod tests {
let qualified_module = inner.project.qualified_module_name(inner.module.path());
let method_pointer = QualifiedMethodPointer {
module: qualified_module.clone(),
defined_on_type: qualified_module,
defined_on_type: qualified_module.clone(),
name: Identifier::from_text("faux").unwrap(),
};
let arguments = vec!["foo".to_owned()];
let faux_vis = Visualization {
id: default(),
expression_id: default(),
module: qualified_module,
method_pointer,
arguments,
};
@ -666,6 +674,7 @@ mod tests {
// We don't attach it separately, as Manager identifies visualizations by their
// expression ID rather than visualization ID.
let desired_vis_3 = Desired {
module: QualifiedName::from_text("local.Widgets.Main").unwrap(),
visualization_id: VisualizationId::from_u128(900),
expression_id: node_id,
metadata: desired_vis_1,

View File

@ -323,6 +323,8 @@ pub struct Visualization {
pub id: VisualizationId,
/// Expression that is to be visualized.
pub expression_id: ExpressionId,
/// Module to evaluate visualization in context of.
pub module: QualifiedName,
/// A pointer to the enso method that will transform the data into expected format.
pub method_pointer: QualifiedMethodPointer,
/// Enso expressions for positional arguments
@ -333,12 +335,13 @@ impl Visualization {
/// Creates a new visualization description. The visualization will get a randomly assigned
/// identifier.
pub fn new(
module: QualifiedName,
expression_id: ExpressionId,
method_pointer: QualifiedMethodPointer,
arguments: Vec<String>,
) -> Visualization {
let id = VisualizationId::new_v4();
Visualization { id, expression_id, method_pointer, arguments }
Visualization { id, expression_id, module, method_pointer, arguments }
}
/// Creates a `VisualizationConfiguration` that is used in communication with language server.
@ -346,6 +349,7 @@ impl Visualization {
let expression = self.method_pointer.clone().into();
let positional_arguments_expressions = self.arguments.clone();
VisualizationConfiguration {
visualization_module: self.module.to_string_with_main_segment(),
execution_context_id,
expression,
positional_arguments_expressions,

View File

@ -532,6 +532,7 @@ pub mod test {
let arguments = vec![];
let vis = Visualization {
id: model::execution_context::VisualizationId::new_v4(),
module: method_pointer.module.clone(),
expression_id: model::execution_context::ExpressionId::new_v4(),
method_pointer,
arguments,
@ -581,6 +582,7 @@ pub mod test {
let arguments = vec!["foo".to_owned()];
let vis = Visualization {
id: model::execution_context::VisualizationId::new_v4(),
module: method_pointer.module.clone(),
expression_id: model::execution_context::ExpressionId::new_v4(),
method_pointer,
arguments,
@ -618,6 +620,7 @@ pub mod test {
let arguments = vec!["bar".to_owned()];
let vis = Visualization {
id: model::execution_context::VisualizationId::new_v4(),
module: method_pointer.module.clone(),
expression_id: model::execution_context::ExpressionId::new_v4(),
method_pointer,
arguments: arguments.clone(),
@ -634,6 +637,7 @@ pub mod test {
let expected_config = language_server::types::VisualizationConfiguration {
execution_context_id: data.context_id,
visualization_module: MockData::new().module_qualified_name().to_string(),
expression: new_expression.clone().into(),
positional_arguments_expressions: arguments.clone(),
};

View File

@ -140,6 +140,7 @@ async fn ls_text_protocol_test() {
execution_context_id,
expression,
positional_arguments_expressions,
visualization_module: visualization_module.to_string(),
};
let response =
client.attach_visualization(&visualization_id, &expression_id, &visualization_config);
@ -157,6 +158,7 @@ async fn ls_text_protocol_test() {
execution_context_id,
expression,
positional_arguments_expressions,
visualization_module: visualization_module.to_string(),
};
let response = client.modify_visualization(&visualization_id, &visualization_config).await;
response.expect("Couldn't modify visualization.");
@ -375,7 +377,8 @@ async fn binary_visualization_updates_test_hlp() {
module_qualified_name,
Identifier::from_text("quux").unwrap(),
);
let visualization = Visualization::new(the_node.id(), method_pointer, vec![]);
let visualization =
Visualization::new(method_pointer.module.clone(), the_node.id(), method_pointer, vec![]);
let stream = graph_executed.attach_visualization(visualization.clone()).await.unwrap();
info!("Attached the visualization {}", visualization.id);
let mut stream = stream.boxed_local();

View File

@ -419,10 +419,10 @@ type_of value = @Builtin_Method "Meta.type_of"
Arguments:
- target: The value or type to get the attribute from.
- method_name: The name of the method or constructor to get the attribute for.
- method: The symbol representing method or constructor to get the attribute for.
- parameter_name: The name of the parameter to get the attribute for.
get_annotation : Any -> Text -> Text -> Any | Nothing
get_annotation target method_name parameter_name = @Builtin_Method "Meta.get_annotation"
get_annotation : Any -> Any -> Text -> Any | Nothing
get_annotation target method parameter_name = @Builtin_Method "Meta.get_annotation"
## PRIVATE
Represents a polyglot language.

View File

@ -4,7 +4,7 @@ from Standard.Base import all
Basic preprocessor for widgets metadata visualization.
Returns full annotation data for all requested arguments.
get_widget_json : Any -> Text -> Vector Text -> Text
get_widget_json : Any -> Any -> Vector Text -> Text
get_widget_json value call_name argument_names =
read_annotation argument =
annotation = Warning.clear <| Meta.get_annotation value call_name argument

View File

@ -453,10 +453,9 @@ interface VisualizationConfiguration {
executionContextId: UUID;
/**
* A qualified name of the module containing the expression which creates
* visualization.
* A qualified name of the module to be used to evaluate the arguments for the visualization expression.
*/
visualizationModule?: String;
visualizationModule: String;
/** An expression that creates a visualization. */
expression: String | MethodPointer;

View File

@ -12,16 +12,16 @@ import java.util.UUID
*
* @param executionContextId an execution context of the visualization
* @param expression an expression that creates a visualization
* @param visualizationModule the name of a module to execute expression at
* @param executionContextId an execution context of the visualization
* @param expression an expression that creates a visualization
*/
case class VisualizationConfiguration(
executionContextId: UUID,
expression: VisualizationExpression
expression: VisualizationExpression,
visualizationModule: String
) extends ToLogString {
/** A qualified module name containing the expression. */
def visualizationModule: String =
expression.module
/** @inheritdoc */
override def toLogString(shouldMask: Boolean): String =
s"VisualizationConfiguration(" +
@ -31,8 +31,9 @@ case class VisualizationConfiguration(
/** Convert to corresponding [[Api]] message. */
def toApi: Api.VisualizationConfiguration =
Api.VisualizationConfiguration(
executionContextId = executionContextId,
expression = expression.toApi
executionContextId = executionContextId,
expression = expression.toApi,
visualizationModule = visualizationModule
)
}
@ -52,7 +53,8 @@ object VisualizationConfiguration {
): VisualizationConfiguration =
new VisualizationConfiguration(
contextId,
VisualizationExpression.Text(module, expression)
VisualizationExpression.Text(module, expression),
module
)
/** Create a visualization configuration.
@ -65,6 +67,7 @@ object VisualizationConfiguration {
*/
def apply(
contextId: UUID,
module: String,
expression: MethodPointer,
positionalArgumentsExpressions: Vector[String]
): VisualizationConfiguration =
@ -73,7 +76,8 @@ object VisualizationConfiguration {
VisualizationExpression.ModuleMethod(
expression,
positionalArgumentsExpressions
)
),
module
)
private object CodecField {
@ -99,11 +103,15 @@ object VisualizationConfiguration {
expression <- cursor
.downField(CodecField.Expression)
.as[MethodPointer]
visualizationModule <- cursor
.downField(CodecField.VisualizationModule)
.as[String]
arguments <- cursor
.downField(CodecField.Arguments)
.as[Option[Vector[String]]]
} yield VisualizationConfiguration(
contextId,
visualizationModule,
expression,
arguments.getOrElse(Vector())
)

View File

@ -204,6 +204,7 @@ object ExecutionContextJsonMessages {
"expressionId": $expressionId,
"visualizationConfig": {
"executionContextId": ${configuration.executionContextId},
"visualizationModule": ${configuration.visualizationModule},
"expression": {
"module": ${methodPointer.module},
"definedOnType": ${methodPointer.definedOnType},
@ -223,6 +224,7 @@ object ExecutionContextJsonMessages {
"expressionId": $expressionId,
"visualizationConfig": {
"executionContextId": ${configuration.executionContextId},
"visualizationModule": ${methodPointer.module},
"expression": {
"module": ${methodPointer.module},
"definedOnType": ${methodPointer.definedOnType},

View File

@ -66,6 +66,7 @@ class VisualizationOperationsTest extends BaseServerTest {
val visualizationConfig =
VisualizationConfiguration(
contextId,
visualizationModule,
MethodPointer(
visualizationModule,
visualizationModule,
@ -120,6 +121,7 @@ class VisualizationOperationsTest extends BaseServerTest {
val visualizationConfig =
VisualizationConfiguration(
contextId,
visualizationModule,
MethodPointer(
visualizationModule,
visualizationModule,

View File

@ -615,21 +615,20 @@ object Runtime {
*
* @param executionContextId an execution context of the visualization
* @param expression the expression that creates a visualization
* @param visualizationModule module to evaluate arguments for visualization at
*/
case class VisualizationConfiguration(
executionContextId: ContextId,
expression: VisualizationExpression
expression: VisualizationExpression,
visualizationModule: String
) extends ToLogString {
/** A qualified module name containing the expression. */
def visualizationModule: String =
expression.module
/** @inheritdoc */
override def toLogString(shouldMask: Boolean): String =
s"VisualizationConfiguration(" +
s"executionContextId=$executionContextId," +
s"expression=${expression.toLogString(shouldMask)})"
s"expression=${expression.toLogString(shouldMask)})" +
s"visualizationModule=${visualizationModule})"
}
/** An operation applied to the suggestion argument. */

View File

@ -59,6 +59,7 @@ class UpsertVisualizationJob(
try {
val maybeCallable =
UpsertVisualizationJob.evaluateVisualizationExpression(
config.visualizationModule,
config.expression
)
@ -180,7 +181,10 @@ object UpsertVisualizationJob {
val expressionId = visualization.expressionId
val visualizationId = visualization.id
val maybeCallable =
evaluateVisualizationExpression(visualization.config.expression)
evaluateVisualizationExpression(
visualizationConfig.visualizationModule,
visualizationConfig.expression
)
maybeCallable.foreach { result =>
updateVisualization(
@ -215,8 +219,9 @@ object UpsertVisualizationJob {
/** Evaluate the visualization expression in a given module.
*
* @param module the module where to evaluate the expression
* @param module the module where to evaluate arguments for the expression
* @param expression the visualization expression
* @param expressionModule the module where to evaluate the expression
* @param retryCount the number of attempted retries
* @param ctx the runtime context
* @return either the evaluation result or an evaluation failure
@ -224,6 +229,7 @@ object UpsertVisualizationJob {
private def evaluateModuleExpression(
module: Module,
expression: Api.VisualizationExpression,
expressionModule: Module,
retryCount: Int = 0
)(implicit
ctx: RuntimeContext
@ -233,7 +239,7 @@ object UpsertVisualizationJob {
val (callback, arguments) = expression match {
case Api.VisualizationExpression.Text(_, expression) =>
val callback = ctx.executionService.evaluateExpression(
module,
expressionModule,
expression
)
val arguments = Vector()
@ -243,7 +249,7 @@ object UpsertVisualizationJob {
argumentExpressions
) =>
val callback = ctx.executionService.prepareFunctionCall(
module,
expressionModule,
QualifiedName.fromString(definedOnType).item,
name
)
@ -261,7 +267,12 @@ object UpsertVisualizationJob {
Level.FINE,
s"Evaluation of visualization was interrupted. Retrying [${retryCount + 1}]."
)
evaluateModuleExpression(module, expression, retryCount + 1)
evaluateModuleExpression(
module,
expression,
expressionModule,
retryCount + 1
)
case error: ThreadInterruptedException =>
val message =
@ -297,18 +308,25 @@ object UpsertVisualizationJob {
/** Evaluate the visualization expression.
*
* @param module module to evaluate the expression arguments at
* @param expression the visualization expression to evaluate
* @param ctx the runtime context
* @return either the evaluation result or an evaluation error
*/
private def evaluateVisualizationExpression(
module: String,
expression: Api.VisualizationExpression
)(implicit
ctx: RuntimeContext
): Either[EvaluationFailure, EvaluationResult] = {
for {
module <- findModule(expression.module)
expression <- evaluateModuleExpression(module, expression)
module <- findModule(module)
expressionModule <- findModule(expression.module)
expression <- evaluateModuleExpression(
module,
expression,
expressionModule
)
} yield expression
}

View File

@ -358,7 +358,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
"Enso_Test.Test.Visualization",
"x -> encode x"
)
),
"Enso_Test.Test.Visualization"
)
)
)
@ -476,7 +477,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
"Enso_Test.Test.Visualization",
"x -> encode x"
)
),
"Enso_Test.Test.Visualization"
)
)
)
@ -611,7 +613,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
"Enso_Test.Test.Visualization",
"x -> encode x"
)
),
"Enso_Test.Test.Visualization"
)
)
)
@ -739,7 +742,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
"Enso_Test.Test.Visualization",
"encode"
)
),
"Enso_Test.Test.Visualization"
)
)
)
@ -867,7 +871,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
"Enso_Test.Test.Visualization",
"encode"
)
),
"Enso_Test.Test.Visualization"
)
)
)
@ -919,7 +924,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
"Enso_Test.Test.Visualization",
"encode"
)
),
"Enso_Test.Test.Visualization"
)
)
)
@ -1085,7 +1091,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
"Enso_Test.Test.Visualization",
"x -> encode x"
)
),
"Enso_Test.Test.Visualization"
)
)
)
@ -1123,7 +1130,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
"Enso_Test.Test.Visualization",
"x -> incAndEncode x"
)
),
"Enso_Test.Test.Visualization"
)
)
)
@ -1192,7 +1200,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
"Enso_Test.Test.Visualization",
"x -> encode x"
)
),
"Enso_Test.Test.Visualization"
)
)
)
@ -1356,7 +1365,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
"Enso_Test.Test.Visualization",
"encode"
)
),
"Enso_Test.Test.Visualization"
)
)
)
@ -1468,7 +1478,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
"Enso_Test.Test.Visualization",
"x -> encode x"
)
),
"Enso_Test.Test.Visualization"
)
)
)
@ -1506,7 +1517,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
"Enso_Test.Test.Visualization",
"x -> incAndEncode x"
)
),
"Enso_Test.Test.Visualization"
)
)
)
@ -1600,7 +1612,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
"Test.Undefined",
"x -> x"
)
),
"Test.Undefined"
)
)
)
@ -1666,7 +1679,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
"Standard.Visualization.Main",
"x -> x.default_visualization.to_text"
)
),
"Standard.Visualization.Main"
)
)
)
@ -1762,7 +1776,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
"Enso_Test.Test.Main",
"Main.does_not_exist"
)
),
"Enso_Test.Test.Main"
)
)
)
@ -1843,7 +1858,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
moduleName,
"x -> x.visualise_me"
)
),
moduleName
)
)
)
@ -1955,7 +1971,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
"Enso_Test.Test.Visualization",
"inc_and_encode"
)
),
"Enso_Test.Test.Visualization"
)
)
)
@ -2071,7 +2088,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
moduleName,
"x -> x.catch_primitive _.to_text"
)
),
moduleName
)
)
)
@ -2170,7 +2188,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
moduleName,
"x -> Panic.catch_primitive x caught_panic-> caught_panic.payload.to_text"
)
),
moduleName
)
)
)
@ -2302,7 +2321,8 @@ class RuntimeVisualizationsTest
visualizationFunction
),
Vector()
)
),
visualizationModule
)
)
)
@ -2403,7 +2423,8 @@ class RuntimeVisualizationsTest
visualizationFunction
),
Vector()
)
),
visualizationModule
)
)
)
@ -2500,7 +2521,8 @@ class RuntimeVisualizationsTest
"incAndEncode"
),
Vector()
)
),
"Enso_Test.Test.Visualization"
)
)
)
@ -2628,7 +2650,8 @@ class RuntimeVisualizationsTest
"incAndEncode"
),
Vector("2", "3")
)
),
"Enso_Test.Test.Visualization"
)
)
)
@ -2698,7 +2721,8 @@ class RuntimeVisualizationsTest
"incAndEncode"
),
Vector("2", "4")
)
),
"Enso_Test.Test.Visualization"
)
)
)
@ -2801,7 +2825,8 @@ class RuntimeVisualizationsTest
"incAndEncode"
),
Vector()
)
),
"Enso_Test.Test.Visualization"
)
)
)
@ -2963,7 +2988,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
"Enso_Test.Test.Main",
"x -> x.to_text"
)
),
"Enso_Test.Test.Main"
)
)
)
@ -3063,7 +3089,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
"Enso_Test.Test.Main",
"x -> x.to_text"
)
),
"Enso_Test.Test.Main"
)
)
)
@ -3191,7 +3218,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
"Enso_Test.Test.Main",
"x -> x.to_text"
)
),
"Enso_Test.Test.Main"
)
)
)
@ -3308,7 +3336,8 @@ class RuntimeVisualizationsTest
Api.VisualizationExpression.Text(
moduleName,
"x -> x.to_text"
)
),
moduleName
)
)
)

View File

@ -1,24 +1,23 @@
package org.enso.interpreter.node.expression.builtin.meta;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
import org.enso.interpreter.node.expression.builtin.text.util.ExpectStringNode;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.Annotation;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.scope.ModuleScope;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.state.State;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.library.CachedLibrary;
@BuiltinMethod(
type = "Meta",
name = "get_annotation",
@ -39,16 +38,22 @@ public abstract class GetAnnotationNode extends BaseNode {
@Cached ThunkExecutorNode thunkExecutorNode,
@Cached ExpectStringNode expectStringNode,
@Cached TypeOfNode typeOfNode) {
String methodName = expectStringNode.execute(method);
Object targetTypeResult = typeOfNode.execute(target);
if (targetTypeResult instanceof DataflowError error) {
return error;
}
if (targetTypeResult instanceof Type targetType) {
ModuleScope scope = targetType.getDefinitionScope();
Function methodFunction = scope.lookupMethodDefinition(targetType, methodName);
Function methodFunction;
if (method instanceof UnresolvedSymbol symbol) {
methodFunction = symbol.resolveFor(targetType);
} else {
CompilerDirectives.transferToInterpreter();
var ctx = EnsoContext.get(this);
var err = ctx.getBuiltins().error();
var payload = err.makeUnsupportedArgumentsError(new Object[] { method }, "Use .name to specify name of function");
throw new PanicException(payload, this);
}
if (methodFunction != null) {
String parameterName = expectStringNode.execute(parameter);
Annotation annotation = methodFunction.getSchema().getAnnotation(parameterName);
@ -59,6 +64,7 @@ public abstract class GetAnnotationNode extends BaseNode {
}
}
if (target instanceof Type type) {
String methodName = ((UnresolvedSymbol) symbol).getName();
AtomConstructor constructor = getAtomConstructor(type, methodName);
if (constructor != null) {
Function constructorFunction = constructor.getConstructorFunction();

View File

@ -282,32 +282,32 @@ spec =
(Test_Type.Value a)==(Test_Type.Value c) . should_be_false
Test.specify "get annotations" <|
Meta.get_annotation Meta_Spec "test_method" "a" . should_equal 7
Meta.get_annotation Meta_Spec "test_method" "b" . should_equal (Test_Type.Value 49)
Meta.get_annotation Meta_Spec "test_method" "c" . should_fail_with Text
Meta.get_annotation Meta_Spec "test_method" "c" . catch . should_equal "Error Value"
Meta.get_annotation Meta_Spec "test_method" "x" . should_equal Nothing
Meta.get_annotation Meta_Spec .test_method "a" . should_equal 7
Meta.get_annotation Meta_Spec .test_method "b" . should_equal (Test_Type.Value 49)
Meta.get_annotation Meta_Spec .test_method "c" . should_fail_with Text
Meta.get_annotation Meta_Spec .test_method "c" . catch . should_equal "Error Value"
Meta.get_annotation Meta_Spec .test_method "x" . should_equal Nothing
value = My_Type.Value 99 "bar" True
Meta.get_annotation value "first_method" "param" . should_equal 11
Meta.get_annotation value "second_method" "param" . should_equal Nothing
Meta.get_annotation value "third_method" "param" . should_equal Nothing
Meta.get_annotation value "other_method" "a" 7 . should_equal 12
Meta.get_annotation value "other_method" "b" value . should_equal 99
Meta.get_annotation value "other_method" "c" . should_equal Nothing
Meta.get_annotation value .first_method "param" . should_equal 11
Meta.get_annotation value .second_method "param" . should_equal Nothing
Meta.get_annotation value .third_method "param" . should_equal Nothing
Meta.get_annotation value .other_method "a" 7 . should_equal 12
Meta.get_annotation value .other_method "b" value . should_equal 99
Meta.get_annotation value .other_method "c" . should_equal Nothing
Meta.get_annotation value "my_method" "self" . should_equal "self"
Meta.get_annotation value .my_method "self" . should_equal "self"
Test.specify "no constructor annotations on value" <|
value = My_Type.Value 99 "bar" True
Meta.get_annotation value "Value" "foo" . should_equal Nothing
Meta.get_annotation value "Value" "bar" . should_equal Nothing
Meta.get_annotation value "Value" "baz" . should_equal Nothing
Meta.get_annotation value .Value "foo" . should_equal Nothing
Meta.get_annotation value .Value "bar" . should_equal Nothing
Meta.get_annotation value .Value "baz" . should_equal Nothing
Test.specify "get annotations on constructor" <|
Meta.get_annotation My_Type "Value" "foo" 7 8 . should_equal 15
Meta.get_annotation My_Type "Value" "bar" . should_equal Nothing
Meta.get_annotation My_Type "Value" "baz" . should_equal (My_Type.Value 1 2 3)
Meta.get_annotation My_Type .Value "foo" 7 8 . should_equal 15
Meta.get_annotation My_Type .Value "bar" . should_equal Nothing
Meta.get_annotation My_Type .Value "baz" . should_equal (My_Type.Value 1 2 3)
Test.group "Check Nothing and NaN" <|
Test.specify "Nothing.is_a Nothing" <|

View File

@ -21,7 +21,7 @@ spec =
Test.group "Widgets for In-Database Connection with table types" <|
Test.specify "works for `tables`" <|
result = Widgets.get_widget_json connection "tables" ["types"]
result = Widgets.get_widget_json connection .tables ["types"]
result.should_contain "'TABLE'"
result.should_contain "'VIEW'"
@ -29,8 +29,8 @@ spec =
Test.specify "works for `query` and `read`" <|
choices = ['a_table', 'another', 'mock_table'] . map n-> Choice.Option n n.pretty
expect = [["query", Widget.Single_Choice choices Nothing Display.Always]] . to_json
Widgets.get_widget_json connection "query" ["query"] . should_equal expect
Widgets.get_widget_json connection "read" ["query"] . should_equal expect
Widgets.get_widget_json connection .query ["query"] . should_equal expect
Widgets.get_widget_json connection .read ["query"] . should_equal expect
Test.group "Widgets for In-Database Table with column name sets" <|
mock_table = connection.query "mock_table"
@ -38,17 +38,17 @@ spec =
Test.specify "works for `get` and `at`" <|
choices = mock_table.column_names . map n-> Choice.Option n n.pretty
expect = [["selector", Widget.Single_Choice choices Nothing Display.Always]] . to_json
Widgets.get_widget_json mock_table "get" ["selector"] . should_equal expect
Widgets.get_widget_json mock_table "at" ["selector"] . should_equal expect
Widgets.get_widget_json mock_table .get ["selector"] . should_equal expect
Widgets.get_widget_json mock_table .at ["selector"] . should_equal expect
Test.specify "works for `filter`" <|
choices = mock_table.column_names . map n-> Choice.Option n n.pretty
expect = [["column", Widget.Single_Choice choices Nothing Display.Always]] . to_json
Widgets.get_widget_json mock_table "filter" ["column"] . should_equal expect
Widgets.get_widget_json mock_table .filter ["column"] . should_equal expect
Test.group "Widgets for Database" <|
Test.specify "works for `connect`" <|
result = Widgets.get_widget_json Database "connect" ["details"]
result = Widgets.get_widget_json Database .connect ["details"]
result.should_contain "SQLite"
result.should_contain "Postgres"
result.should_contain "Redshift"

View File

@ -18,12 +18,12 @@ spec =
Test.specify "works for `get` and `at`" <|
choices = mock_table.column_names . map n-> Choice.Option n n.pretty
expect = [["selector", Widget.Single_Choice choices Nothing Display.Always]] . to_json
Widgets.get_widget_json mock_table "get" ["selector"] . should_equal expect
Widgets.get_widget_json mock_table "at" ["selector"] . should_equal expect
Widgets.get_widget_json mock_table .get ["selector"] . should_equal expect
Widgets.get_widget_json mock_table .at ["selector"] . should_equal expect
Test.specify "works for `filter`" <|
choices = mock_table.column_names . map n-> Choice.Option n n.pretty
expect = [["column", Widget.Single_Choice choices Nothing Display.Always]] . to_json
Widgets.get_widget_json mock_table "filter" ["column"] . should_equal expect
Widgets.get_widget_json mock_table .filter ["column"] . should_equal expect
main = Test_Suite.run_main spec

View File

@ -16,9 +16,9 @@ spec =
mock_text = "abc def"
default_widget = Text_Sub_Range.default_widget
expect = [["range", default_widget]] . to_json
json = Widgets.get_widget_json mock_text "take" ["range"]
json = Widgets.get_widget_json mock_text .take ["range"]
json . should_equal expect
Widgets.get_widget_json mock_text "drop" ["range"] . should_equal expect
Widgets.get_widget_json mock_text .drop ["range"] . should_equal expect
obj = json.parse_json
widget = obj.first.second
options = widget . at "values"