Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 34 additions & 5 deletions src/DynamoCore/Graph/Nodes/CodeBlockNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public class CodeBlockNodeModel : NodeModel
private string code = string.Empty;
private List<string> inputIdentifiers = new List<string>();
private List<string> inputPortNames = new List<string>();
private List<string> outputPortNames = new List<string>();
private List<string> outputPortTooltips = new List<string>();
private string previewVariable;
private readonly LibraryServices libraryServices;

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

codeStatements.Clear();
outputPortNames.Clear();
outputPortTooltips.Clear();

try
{
var priorNames = libraryServices.GetPriorNames();
Expand All @@ -797,7 +802,20 @@ private void ProcessCode(out string errorMessage, out string warningMessage,
{
// Create a statement variable from the generated nodes
codeStatements.Add(Statement.CreateInstance(parsedNode));
}

if (parsedNode is BinaryExpressionNode binExp)
{
var right = binExp.RightNode;
var left = binExp.LeftNode;

var portLabel = IsTempIdentifier(left.Name)
? CodeBlockUtils.InferStaticTypeFromNode(right)
: left.Name;

outputPortNames.Add(portLabel);
}
}
outputPortTooltips = CodeBlockUtils.GetCleanedCodeExpressionsForTooltips(code);
}
else
{
Expand Down Expand Up @@ -1007,15 +1025,26 @@ private void SetOutputPorts()
// Clear out all the output port models
OutPorts.RemoveAll((p) => true);

int i = 0;

foreach (var def in allDefs)
{
if (i >= outputPortNames.Count) break;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a finite loop. It runs for each def in allDefs. Why are you breaking out of it in between?


var label = outputPortNames[i];
var tooltip = IsTempIdentifier(def.Key) ? string.Format(Resources.CodeBlockTempIdentifierOutputLabel, def.Value) : def.Key;

OutPorts.Add(new PortModel(PortType.Output, this, new PortData(string.Empty, tooltip)
// Trim long labels
int maxLength = Configurations.CBNMaxPortNameLength;
if (label.Length > maxLength) label = label.Remove(maxLength - 3) + "...";

OutPorts.Add(new PortModel(PortType.Output, this, new PortData(label, tooltip)
{
LineIndex = def.Value - 1, // Logical line index.
Height = Configurations.CodeBlockOutputPortHeightInPixels,
}));

i++;
}
}

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



private class IdentifierInPlaceMapper : AstReplacer
{
Expand Down Expand Up @@ -1496,7 +1525,7 @@ public static void GetReferencedVariables(Node astNode, List<Variable> refVariab
//Or node not completely implemented YET
}
}

/// <summary>
/// Returns the names of the variables that have been declared in the statement
/// </summary>
Expand Down Expand Up @@ -1708,7 +1737,7 @@ public Variable(IdentifierNode identNode)
Row = identNode.line;
StartColumn = identNode.col;
}

/// <summary>
/// Moves column index back only if variable is not an expression.
/// </summary>
Expand Down
67 changes: 67 additions & 0 deletions src/DynamoCore/Graph/Nodes/CodeBlockUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Text.RegularExpressions;
using Dynamo.Configuration;
using ProtoCore.AST.AssociativeAST;

namespace Dynamo.Graph.Nodes
{
Expand Down Expand Up @@ -265,6 +266,72 @@ internal static IOrderedEnumerable<KeyValuePair<string, int>> GetDefinitionLineI

return locationMap.OrderBy(p => p.Value);
}

/// <summary>
/// Extracts cleaned individual code expressions as tooltip strings from a full code block.
/// </summary>
internal static List<string> GetCleanedCodeExpressionsForTooltips(string code)
{
if (string.IsNullOrWhiteSpace(code))
return new List<string>();

// Filter out full-line comments and remove inline comments
var cleanedCode = string.Join(" ",
code.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Where(line => !line.TrimStart().StartsWith("//"))
.Select(line =>
{
var idx = line.IndexOf("//");
return idx >= 0 ? line.Substring(0, idx) : line;
}));

// Split by semicolon and return trimmed expressions
return cleanedCode
.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
.Select(expr => expr.Trim())
.Where(expr => !string.IsNullOrEmpty(expr))
.Select(expr => expr + ";")
.ToList();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm afraid this function needs to be entirely rewritten, as it shouldn't be using custom string manipulation when we have a full-fledged parser for DesignScript code and syntax. Will send another PR to clean this up.


/// <summary>
/// Infers a human-readable static type label (e.g., [int], [string]) from an associative AST node.
/// </summary>
internal static string InferStaticTypeFromNode(AssociativeNode node)
{
if (node == null) return null;

switch (node)
{
case IntNode:
return "[int]";
case DoubleNode:
return "[double]";
case BooleanNode:
return "[bool]";
case StringNode:
return "[string]";
case NullNode:
return "[null]";
case CharNode:
return "[char]";
case ExprListNode:
case ArrayNode:
return "[list]";
case RangeExprNode:
return "[range]";
case IdentifierNode idNode:
return idNode.Value;
case IdentifierListNode:
return "[identifier list]";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe this is a user-friendly description. I'm confident that many users will not understand this technical wording.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense, what will be your suggestion in this case?

@aparajit-pratap aparajit-pratap Jul 9, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no straightforward answer, unfortunately. An IdentifierListNode can be a function call (DSCore.List.Sort()), or property invocation (circle.CenterPoint.X). It's essentially any complex expression containing a chain (list) of identifiers, where each identifier can either be a namespace, class, variable, function, or property. I suppose the ideal solution here would be to break down the identifier list and inspect its rightmost AST to see if it's a function or property (a getter function in DS), then to label it as either a function or property.

case FunctionCallNode:
return "[function]";
case InlineConditionalNode:
return "[conditional]";
default:
return "[unknown]";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about imperative language blocks?

[Imperative]
{ ... }

}
}
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/DynamoCore/Properties/Resources.en-US.resx
Original file line number Diff line number Diff line change
Expand Up @@ -926,4 +926,4 @@ This package likely contains an assembly that is blocked. You will need to load
<data name="ToastFileNodeAutoCompleteDoubleClick" xml:space="preserve">
<value>Click on the sparkle icon next to the port to activate Node Autocomplete.</value>
</data>
</root>
</root>
2 changes: 1 addition & 1 deletion src/DynamoCore/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -929,4 +929,4 @@ This package likely contains an assembly that is blocked. You will need to load
<data name="ToastFileNodeAutoCompleteDoubleClick" xml:space="preserve">
<value>Click on the sparkle icon next to the port to activate Node Autocomplete.</value>
</data>
</root>
</root>
21 changes: 10 additions & 11 deletions src/DynamoCoreWpf/Controls/OutPorts.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -304,18 +304,17 @@ private void OnDataContextChanged(object sender, DependencyPropertyChangedEventA
if (viewModel.IsPortCondensed)
{
MainGrid.Height = 14;
MainGrid.Margin = new Thickness(0,3,0,0);

PortBackgroundBorder.CornerRadius = new CornerRadius(0);
PortBackgroundBorder.BorderThickness = new Thickness(0);
PortBackgroundBorder.Height = 14;
PortBackgroundBorder.Width = 20;
PortBackgroundBorder.Background = _midGrey;
portBackGroundColor = _midGrey;
PortBackgroundBorder.BorderBrush = Brushes.Transparent;
PortNameTextBox.Margin = new Thickness(12,1,0,0);
MainGrid.Margin = new Thickness(0, 3, 0, 0);

PortBackgroundBorder.CornerRadius = new CornerRadius(4, 0, 0, 4);
PortBackgroundBorder.Height = 13;

PortNameGrid.Height = 14;
PortNameGrid.Margin = new Thickness(0, 1, 2, 0);
PortNameGrid.Margin = new Thickness(0, 2, 2, 0);
PortNameTextBox.Margin = new Thickness(9, 0, 0, 1);
PortNameTextBox.FontSize = 9;
PortNameTextBox.MaxWidth = 100;
PortNameTextBox.TextTrimming = TextTrimming.CharacterEllipsis;
}
}

Expand Down
9 changes: 8 additions & 1 deletion src/DynamoCoreWpf/ViewModels/Core/AnnotationViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ public class AnnotationViewModel : ViewModelBase
private IEnumerable<Configuration.StyleItem> preferencesStyleItemsList;
private PreferenceSettings preferenceSettings;

// Collapsed proxy ports for Code Block Nodes appear visually misaligned - 0.655px
// taller compared to their actual ports. This is due to the fixed height - 16.345px
// used inside CBNs for code lines, while proxy ports use 14px height + 3px top margin.
// To compensate for this visual mismatch and keep connector alignment consistent,
// we apply this adjusted proxy height.
private const double CodeBlockCollapsedPortVisualHeight = 17;
Comment thread
jasonstratton marked this conversation as resolved.

public readonly WorkspaceViewModel WorkspaceViewModel;

#region Properties
Expand Down Expand Up @@ -809,7 +816,7 @@ private Point2D CalculatePortPosition(PortModel portModel, double verticalPositi
if (portModel.Owner is CodeBlockNodeModel)
{
// Special case because code block outputs are smaller than regular outputs.
return new Point2D(Left + Width, y - 8);
return new Point2D(Left + Width, y - 7);
}
return new Point2D(Left + Width, y);
}
Expand Down
2 changes: 1 addition & 1 deletion test/core/dummy_node/2080_JSONTESTCRASH undo_redo.dyn
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"Outputs": [
{
"Id": "8234ec6cc018433da0605ddfb17cdc2b",
"Name": "",
"Name": "[int]",
"Description": "Value of expression at line 1",
"UsingDefaultValue": false,
"Level": 2,
Expand Down
Loading