@@ -68651,17 +68651,18 @@ azdataGraph.prototype.getStyledTooltipForCell = function (cell) {
6865168651
6865268652 // tooltip heading for vertices only
6865368653 if (!cell.edge) {
68654- tooltip += `<div style=\"${centerText}\"><span style=\"${boldText}\">${cell.value.tooltipTitle}</span></div>`;
68655- if(cell.value.description){
68654+ let tooltipTitle = this.truncateTooltipTitle(cell.value.tooltipTitle);
68655+ tooltip += `<div style=\"${centerText}\"><span style=\"${boldText}\">${tooltipTitle}</span></div>`;
68656+ if (cell.value.description) {
6865668657 tooltip += `<div style=\"${headerBottomMargin} ${headerTopMargin}\"><span>${cell.value.description}</span></div>`;
6865768658 }
68658- }
68659+ }
6865968660
6866068661 // tooltip body
6866168662 let startIndex = cell.edge ? 0 : 1; // first index for vertices contains footer label, so we can skip for vertices.
6866268663 for (var i = startIndex; i < cell.value.metrics.length; ++i) {
68663- if(cell.value.metrics[i].isLongString){ // Skipping all strings as they go to the bottom of tooltip
68664- continue;
68664+ if (cell.value.metrics[i].isLongString) { // Skipping all strings as they go to the bottom of tooltip
68665+ continue;
6866568666 }
6866668667 tooltip += `<div style=\"${tooltipLineHeight}\">`;
6866768668
@@ -68674,26 +68675,53 @@ azdataGraph.prototype.getStyledTooltipForCell = function (cell) {
6867468675 tooltip += `<hr />`;
6867568676 }
6867668677
68677- tooltip += `</div>`
68678+ tooltip += `</div>`;
6867868679 }
6867968680
6868068681 // tooltip footer for vertices only
6868168682 if (!cell.edge) {
6868268683 cell.value.metrics.filter(m => m.isLongString).forEach(m => {
6868368684 tooltip += '<hr />';
6868468685 tooltip += `<div style=\"${footerTopMargin}\"><span style=\"${boldText}\">${m.name}</span></div>`;
68685- tooltip += `<div><span>${m.value.replace(/(\r\n|\n|\r)/gm, " ")}</span></div>`; // Removing all line breaks as they look bad in tooltips
68686+
68687+ let metricLabel = m.value.replace(/(\r\n|\n|\r)/gm, " ");
68688+ if (metricLabel.length > 103) {
68689+ metricLabel = metricLabel.substring(0, 100) + '...';
68690+ }
68691+ tooltip += `<div><span>${metricLabel}</span></div>`; // Removing all line breaks as they look bad in tooltips
6868668692 })
6868768693 }
6868868694
6868968695 tooltip += '</div>';
68690-
68696+
6869168697 return tooltip;
6869268698 }
6869368699
6869468700 return azdataGraph.prototype.getTooltipForCell.apply(this, arguments); // "supercall"
6869568701};
6869668702
68703+ azdataGraph.prototype.truncateTooltipTitle = function (title) {
68704+ let hasWindowsEOL = title.includes('\r\n');
68705+ let titleSegments = hasWindowsEOL ? title.split('\r\n') : title.split('\n');
68706+ let truncatedTitleSegments = titleSegments.map(segment => {
68707+ if (segment.length > 50) {
68708+ return segment.substring(0, 50) + '...';
68709+ }
68710+ else {
68711+ return segment;
68712+ }
68713+ });
68714+
68715+ if (hasWindowsEOL) {
68716+ title = truncatedTitleSegments.join('\r\n');
68717+ }
68718+ else {
68719+ title = truncatedTitleSegments.join('\n');
68720+ }
68721+
68722+ return title;
68723+ };
68724+
6869768725/**
6869868726 * Function: graphEventHandler
6869968727 *
@@ -68724,7 +68752,7 @@ azdataGraph.prototype.graphEventHandler = function (sender, event, eventCallback
6872468752 * eventType - The event type (i.e. 'click') that should trigger the callback
6872568753 * callback - The callback function that is executed by the event listener.
6872668754 */
68727- azdataGraph.prototype.addDomEventListener = function (element, eventType, eventCallback) {
68755+ azdataGraph.prototype.addDomEventListener = function (element, eventType, eventCallback) {
6872868756 mxEvent.addListener(element, eventType, (e) => {
6872968757 if (eventCallback) {
6873068758 eventCallback();
@@ -92861,6 +92889,12 @@ const CELL_HEIGHT = 70;
9286192889const STANDARD_NODE_DISTANCE = 173;
9286292890const IDEAL_LONG_LABEL_NODE_DISTANCE = 240;
9286392891
92892+ // Setting this to 38 because SSMS truncates labels longer than 38 characters
92893+ const LABEL_LENGTH_LIMIT = 38;
92894+
92895+ const NODE_HEIGHT = 105;
92896+ const NODE_WIDTH = 100;
92897+
9286492898class PolygonRoot {
9286592899 constructor(cell, fillColor, strokeColor, strokeWidth) {
9286692900 this.cell = cell;
@@ -93151,20 +93185,46 @@ azdataQueryPlan.prototype.init = function (container, iconPaths, badgeIconPaths)
9315193185 graph.convertValueToString = function (cell) {
9315293186 if (cell.value != null && cell.value.label != null) {
9315393187 let hasWindowsEOL = cell.value.label.includes('\r\n');
93188+ const joinStrings = (strArray) => {
93189+ if (hasWindowsEOL) {
93190+ return strArray.join('\r\n');
93191+ }
93192+ else {
93193+ return strArray.join('\n');
93194+ }
93195+ };
93196+
93197+
9315493198 let splitLabel = cell.value.label.split(/\r\n|\n/);
93155- let cellLabel = splitLabel.map(str => {
93199+ let cellLabel = splitLabel.map(( str, index) => {
9315693200 let label = '';
93157- label += str;
93201+ if (index === 0 && !cell.value.icon?.includes('columnstore')) {
93202+ // This regex removes any text contained in parenthesis in the operation name
93203+ // i.e. "Clustered Index Seek (Clustered)" becomes "Clustered Index Seek"
93204+ label += str.replace(/\(([^)]+)\)/g, '');
93205+ }
93206+ else if (index === 1 && splitLabel.length >= 3 && str.includes('.')) {
93207+ let splitStr = str.split(' ');
93208+ splitStr = splitStr.map(str => {
93209+ if (str.length >= LABEL_LENGTH_LIMIT) {
93210+ // subtracting 3 for ellipse to fit in character limit
93211+ return str.substring(0, LABEL_LENGTH_LIMIT - 3) + '...';
93212+ }
93213+ else {
93214+ return str;
93215+ }
93216+ });
93217+
93218+ label += joinStrings(splitStr);
93219+ }
93220+ else {
93221+ label += str;
93222+ }
9315893223
9315993224 return label;
9316093225 });
9316193226
93162- if (hasWindowsEOL) {
93163- cellLabel = cellLabel.join('\r\n');
93164- }
93165- else {
93166- cellLabel = cellLabel.join('\n');
93167- }
93227+ cellLabel = joinStrings(cellLabel);
9316893228
9316993229 return cellLabel;
9317093230 }
@@ -93289,7 +93349,7 @@ azdataQueryPlan.prototype.placeGraphNodes = function () {
9328993349 // for entire showplan. For starters, we set this to 100px. However,
9329093350 // if a node has label with many lines, this value will be updated to
9329193351 // better fit that node.
93292- this.spacingY = 100 ;
93352+ this.spacingY = NODE_HEIGHT ;
9329393353
9329493354 // Getting the node padding values from SSMS.
9329593355 this.paddingX = GRAPH_PADDING_RIGHT;
@@ -93374,13 +93434,28 @@ azdataQueryPlan.prototype.adjustGraphNodeHorizontalPositions = function (node) {
9337493434 Object.keys(levelsTable).map(key => {
9337593435 for (let levelNodeIndex = 1; levelNodeIndex < levelsTable[key].length; ++levelNodeIndex) {
9337693436 let previousNode = levelsTable[key][levelNodeIndex - 1];
93377- let currentNode = levelsTable[key][levelNodeIndex]
93437+ let currentNode = levelsTable[key][levelNodeIndex];
9337893438
9337993439 let previousLabel = previousNode.label.split(/\r\n|\n/).filter(str => str.length > 20);
9338093440 if (previousLabel.length !== 0) {
93441+ let longestString = '';
93442+ let labelPartitions = currentNode.label.split(/\r\n|\n/g);
93443+ labelPartitions.forEach(str => {
93444+ if (longestString.length < str.length) {
93445+ longestString = str;
93446+ }
93447+ });
93448+
93449+ var size = mxUtils.getSizeForString(longestString.substring(0, LABEL_LENGTH_LIMIT),
93450+ mxConstants.DEFAULT_FONTSIZE,
93451+ mxConstants.DEFAULT_FONTFAMILY,
93452+ undefined,
93453+ mxConstants.DEFAULT_FONTSTYLE);
93454+
9338193455 let distanceFromPreviousNode = currentNode.position.x - previousNode.position.x;
93456+
9338293457 if (distanceFromPreviousNode <= STANDARD_NODE_DISTANCE) {
93383- let shiftToRightAmount = IDEAL_LONG_LABEL_NODE_DISTANCE - distanceFromPreviousNode;
93458+ let shiftToRightAmount = Math.max(size.width, IDEAL_LONG_LABEL_NODE_DISTANCE) - distanceFromPreviousNode;
9338493459 currentNode.position.x += shiftToRightAmount;
9338593460
9338693461 this.shiftParentAndChildNodePositionsHorizontally(currentNode.parent, shiftToRightAmount);
@@ -93631,9 +93706,6 @@ azdataQueryPlan.prototype.getPolygonPerimeter = function (cell) {
9363193706 return points;
9363293707}
9363393708
93634- const NODE_HEIGHT = 100;
93635- const NODE_WIDTH = 100;
93636-
9363793709/**
9363893710 * Gets the left side points for the starting node in the polygon from top to bottom.
9363993711 * @param {*} cell The starting node for the left side perimeter points.
@@ -93729,6 +93801,9 @@ azdataQueryPlan.prototype.getRightSidePoints = function (cell) {
9372993801
9373093802azdataQueryPlan.prototype.calcAdditionalSpacingForNode = function(cell) {
9373193803 let longestSubLabel = Math.max(...(cell.value.label.split(/\r\n|\n/).map(str => str.length)));
93804+ if (longestSubLabel > LABEL_LENGTH_LIMIT) {
93805+ longestSubLabel = LABEL_LENGTH_LIMIT;
93806+ }
9373293807 // These values to work best for drawing regions around labels of different lengths, so the label is always inside the polygon.
9373393808 return longestSubLabel / 10 * 15;
9373493809}
0 commit comments