Skip to content

Commit 1fc55b5

Browse files
authored
[DYN-8997] CBN output port to show the name of the variable (#16300)
1 parent 6d43d82 commit 1fc55b5

7 files changed

Lines changed: 122 additions & 20 deletions

File tree

src/DynamoCore/Graph/Nodes/CodeBlockNode.cs

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ public class CodeBlockNodeModel : NodeModel
4141
private string code = string.Empty;
4242
private List<string> inputIdentifiers = new List<string>();
4343
private List<string> inputPortNames = new List<string>();
44+
private List<string> outputPortNames = new List<string>();
45+
private List<string> outputPortTooltips = new List<string>();
4446
private string previewVariable;
4547
private readonly LibraryServices libraryServices;
4648

@@ -783,6 +785,9 @@ private void ProcessCode(out string errorMessage, out string warningMessage,
783785
ParseParam = new ParseParam(GUID, code, resolver);
784786

785787
codeStatements.Clear();
788+
outputPortNames.Clear();
789+
outputPortTooltips.Clear();
790+
786791
try
787792
{
788793
var priorNames = libraryServices.GetPriorNames();
@@ -797,7 +802,20 @@ private void ProcessCode(out string errorMessage, out string warningMessage,
797802
{
798803
// Create a statement variable from the generated nodes
799804
codeStatements.Add(Statement.CreateInstance(parsedNode));
800-
}
805+
806+
if (parsedNode is BinaryExpressionNode binExp)
807+
{
808+
var right = binExp.RightNode;
809+
var left = binExp.LeftNode;
810+
811+
var portLabel = IsTempIdentifier(left.Name)
812+
? CodeBlockUtils.InferStaticTypeFromNode(right)
813+
: left.Name;
814+
815+
outputPortNames.Add(portLabel);
816+
}
817+
}
818+
outputPortTooltips = CodeBlockUtils.GetCleanedCodeExpressionsForTooltips(code);
801819
}
802820
else
803821
{
@@ -1007,15 +1025,26 @@ private void SetOutputPorts()
10071025
// Clear out all the output port models
10081026
OutPorts.RemoveAll((p) => true);
10091027

1028+
int i = 0;
1029+
10101030
foreach (var def in allDefs)
10111031
{
1032+
if (i >= outputPortNames.Count) break;
1033+
1034+
var label = outputPortNames[i];
10121035
var tooltip = IsTempIdentifier(def.Key) ? string.Format(Resources.CodeBlockTempIdentifierOutputLabel, def.Value) : def.Key;
10131036

1014-
OutPorts.Add(new PortModel(PortType.Output, this, new PortData(string.Empty, tooltip)
1037+
// Trim long labels
1038+
int maxLength = Configurations.CBNMaxPortNameLength;
1039+
if (label.Length > maxLength) label = label.Remove(maxLength - 3) + "...";
1040+
1041+
OutPorts.Add(new PortModel(PortType.Output, this, new PortData(label, tooltip)
10151042
{
10161043
LineIndex = def.Value - 1, // Logical line index.
10171044
Height = Configurations.CodeBlockOutputPortHeightInPixels,
10181045
}));
1046+
1047+
i++;
10191048
}
10201049
}
10211050

@@ -1269,7 +1298,7 @@ private string LocalizeIdentifier(string identifierName)
12691298
return string.Format("{0}_{1}", identifierName, AstIdentifierGuid);
12701299
}
12711300

1272-
1301+
12731302

12741303
private class IdentifierInPlaceMapper : AstReplacer
12751304
{
@@ -1496,7 +1525,7 @@ public static void GetReferencedVariables(Node astNode, List<Variable> refVariab
14961525
//Or node not completely implemented YET
14971526
}
14981527
}
1499-
1528+
15001529
/// <summary>
15011530
/// Returns the names of the variables that have been declared in the statement
15021531
/// </summary>
@@ -1708,7 +1737,7 @@ public Variable(IdentifierNode identNode)
17081737
Row = identNode.line;
17091738
StartColumn = identNode.col;
17101739
}
1711-
1740+
17121741
/// <summary>
17131742
/// Moves column index back only if variable is not an expression.
17141743
/// </summary>

src/DynamoCore/Graph/Nodes/CodeBlockUtils.cs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Linq;
44
using System.Text.RegularExpressions;
55
using Dynamo.Configuration;
6+
using ProtoCore.AST.AssociativeAST;
67

78
namespace Dynamo.Graph.Nodes
89
{
@@ -265,6 +266,72 @@ internal static IOrderedEnumerable<KeyValuePair<string, int>> GetDefinitionLineI
265266

266267
return locationMap.OrderBy(p => p.Value);
267268
}
269+
270+
/// <summary>
271+
/// Extracts cleaned individual code expressions as tooltip strings from a full code block.
272+
/// </summary>
273+
internal static List<string> GetCleanedCodeExpressionsForTooltips(string code)
274+
{
275+
if (string.IsNullOrWhiteSpace(code))
276+
return new List<string>();
277+
278+
// Filter out full-line comments and remove inline comments
279+
var cleanedCode = string.Join(" ",
280+
code.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
281+
.Where(line => !line.TrimStart().StartsWith("//"))
282+
.Select(line =>
283+
{
284+
var idx = line.IndexOf("//");
285+
return idx >= 0 ? line.Substring(0, idx) : line;
286+
}));
287+
288+
// Split by semicolon and return trimmed expressions
289+
return cleanedCode
290+
.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
291+
.Select(expr => expr.Trim())
292+
.Where(expr => !string.IsNullOrEmpty(expr))
293+
.Select(expr => expr + ";")
294+
.ToList();
295+
}
296+
297+
/// <summary>
298+
/// Infers a human-readable static type label (e.g., [int], [string]) from an associative AST node.
299+
/// </summary>
300+
internal static string InferStaticTypeFromNode(AssociativeNode node)
301+
{
302+
if (node == null) return null;
303+
304+
switch (node)
305+
{
306+
case IntNode:
307+
return "[int]";
308+
case DoubleNode:
309+
return "[double]";
310+
case BooleanNode:
311+
return "[bool]";
312+
case StringNode:
313+
return "[string]";
314+
case NullNode:
315+
return "[null]";
316+
case CharNode:
317+
return "[char]";
318+
case ExprListNode:
319+
case ArrayNode:
320+
return "[list]";
321+
case RangeExprNode:
322+
return "[range]";
323+
case IdentifierNode idNode:
324+
return idNode.Value;
325+
case IdentifierListNode:
326+
return "[identifier list]";
327+
case FunctionCallNode:
328+
return "[function]";
329+
case InlineConditionalNode:
330+
return "[conditional]";
331+
default:
332+
return "[unknown]";
333+
}
334+
}
268335
}
269336

270337
/// <summary>

src/DynamoCore/Properties/Resources.en-US.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -926,4 +926,4 @@ This package likely contains an assembly that is blocked. You will need to load
926926
<data name="ToastFileNodeAutoCompleteDoubleClick" xml:space="preserve">
927927
<value>Click on the sparkle icon next to the port to activate Node Autocomplete.</value>
928928
</data>
929-
</root>
929+
</root>

src/DynamoCore/Properties/Resources.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -929,4 +929,4 @@ This package likely contains an assembly that is blocked. You will need to load
929929
<data name="ToastFileNodeAutoCompleteDoubleClick" xml:space="preserve">
930930
<value>Click on the sparkle icon next to the port to activate Node Autocomplete.</value>
931931
</data>
932-
</root>
932+
</root>

src/DynamoCoreWpf/Controls/OutPorts.xaml.cs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -304,18 +304,17 @@ private void OnDataContextChanged(object sender, DependencyPropertyChangedEventA
304304
if (viewModel.IsPortCondensed)
305305
{
306306
MainGrid.Height = 14;
307-
MainGrid.Margin = new Thickness(0,3,0,0);
308-
309-
PortBackgroundBorder.CornerRadius = new CornerRadius(0);
310-
PortBackgroundBorder.BorderThickness = new Thickness(0);
311-
PortBackgroundBorder.Height = 14;
312-
PortBackgroundBorder.Width = 20;
313-
PortBackgroundBorder.Background = _midGrey;
314-
portBackGroundColor = _midGrey;
315-
PortBackgroundBorder.BorderBrush = Brushes.Transparent;
316-
PortNameTextBox.Margin = new Thickness(12,1,0,0);
307+
MainGrid.Margin = new Thickness(0, 3, 0, 0);
308+
309+
PortBackgroundBorder.CornerRadius = new CornerRadius(4, 0, 0, 4);
310+
PortBackgroundBorder.Height = 13;
311+
317312
PortNameGrid.Height = 14;
318-
PortNameGrid.Margin = new Thickness(0, 1, 2, 0);
313+
PortNameGrid.Margin = new Thickness(0, 2, 2, 0);
314+
PortNameTextBox.Margin = new Thickness(9, 0, 0, 1);
315+
PortNameTextBox.FontSize = 9;
316+
PortNameTextBox.MaxWidth = 100;
317+
PortNameTextBox.TextTrimming = TextTrimming.CharacterEllipsis;
319318
}
320319
}
321320

src/DynamoCoreWpf/ViewModels/Core/AnnotationViewModel.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ public class AnnotationViewModel : ViewModelBase
3333
private IEnumerable<Configuration.StyleItem> preferencesStyleItemsList;
3434
private PreferenceSettings preferenceSettings;
3535

36+
// Collapsed proxy ports for Code Block Nodes appear visually misaligned - 0.655px
37+
// taller compared to their actual ports. This is due to the fixed height - 16.345px
38+
// used inside CBNs for code lines, while proxy ports use 14px height + 3px top margin.
39+
// To compensate for this visual mismatch and keep connector alignment consistent,
40+
// we apply this adjusted proxy height.
41+
private const double CodeBlockCollapsedPortVisualHeight = 17;
42+
3643
public readonly WorkspaceViewModel WorkspaceViewModel;
3744

3845
#region Properties
@@ -809,7 +816,7 @@ private Point2D CalculatePortPosition(PortModel portModel, double verticalPositi
809816
if (portModel.Owner is CodeBlockNodeModel)
810817
{
811818
// Special case because code block outputs are smaller than regular outputs.
812-
return new Point2D(Left + Width, y - 8);
819+
return new Point2D(Left + Width, y - 7);
813820
}
814821
return new Point2D(Left + Width, y);
815822
}

test/core/dummy_node/2080_JSONTESTCRASH undo_redo.dyn

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
"Outputs": [
3838
{
3939
"Id": "8234ec6cc018433da0605ddfb17cdc2b",
40-
"Name": "",
40+
"Name": "[int]",
4141
"Description": "Value of expression at line 1",
4242
"UsingDefaultValue": false,
4343
"Level": 2,

0 commit comments

Comments
 (0)