Skip to content

Commit fbee58a

Browse files
Release 0.0.28 (#88)
1 parent 972e1f3 commit fbee58a

6 files changed

Lines changed: 295 additions & 70 deletions

File tree

dist/build.js

Lines changed: 98 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -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;
9286192889
const STANDARD_NODE_DISTANCE = 173;
9286292890
const 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+
9286492898
class 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

9373093802
azdataQueryPlan.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
}

dist/js/azdata/azdataQueryPlan.js

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ const CELL_HEIGHT = 70;
1212
const STANDARD_NODE_DISTANCE = 173;
1313
const IDEAL_LONG_LABEL_NODE_DISTANCE = 240;
1414

15+
// Setting this to 38 because SSMS truncates labels longer than 38 characters
16+
const LABEL_LENGTH_LIMIT = 38;
17+
18+
const NODE_HEIGHT = 105;
19+
const NODE_WIDTH = 100;
20+
1521
class PolygonRoot {
1622
constructor(cell, fillColor, strokeColor, strokeWidth) {
1723
this.cell = cell;
@@ -302,20 +308,46 @@ azdataQueryPlan.prototype.init = function (container, iconPaths, badgeIconPaths)
302308
graph.convertValueToString = function (cell) {
303309
if (cell.value != null && cell.value.label != null) {
304310
let hasWindowsEOL = cell.value.label.includes('\r\n');
311+
const joinStrings = (strArray) => {
312+
if (hasWindowsEOL) {
313+
return strArray.join('\r\n');
314+
}
315+
else {
316+
return strArray.join('\n');
317+
}
318+
};
319+
320+
305321
let splitLabel = cell.value.label.split(/\r\n|\n/);
306-
let cellLabel = splitLabel.map(str => {
322+
let cellLabel = splitLabel.map((str, index) => {
307323
let label = '';
308-
label += str;
324+
if (index === 0 && !cell.value.icon?.includes('columnstore')) {
325+
// This regex removes any text contained in parenthesis in the operation name
326+
// i.e. "Clustered Index Seek (Clustered)" becomes "Clustered Index Seek"
327+
label += str.replace(/\(([^)]+)\)/g, '');
328+
}
329+
else if (index === 1 && splitLabel.length >= 3 && str.includes('.')) {
330+
let splitStr = str.split(' ');
331+
splitStr = splitStr.map(str => {
332+
if (str.length >= LABEL_LENGTH_LIMIT) {
333+
// subtracting 3 for ellipse to fit in character limit
334+
return str.substring(0, LABEL_LENGTH_LIMIT - 3) + '...';
335+
}
336+
else {
337+
return str;
338+
}
339+
});
340+
341+
label += joinStrings(splitStr);
342+
}
343+
else {
344+
label += str;
345+
}
309346

310347
return label;
311348
});
312349

313-
if (hasWindowsEOL) {
314-
cellLabel = cellLabel.join('\r\n');
315-
}
316-
else {
317-
cellLabel = cellLabel.join('\n');
318-
}
350+
cellLabel = joinStrings(cellLabel);
319351

320352
return cellLabel;
321353
}
@@ -440,7 +472,7 @@ azdataQueryPlan.prototype.placeGraphNodes = function () {
440472
// for entire showplan. For starters, we set this to 100px. However,
441473
// if a node has label with many lines, this value will be updated to
442474
// better fit that node.
443-
this.spacingY = 100;
475+
this.spacingY = NODE_HEIGHT;
444476

445477
// Getting the node padding values from SSMS.
446478
this.paddingX = GRAPH_PADDING_RIGHT;
@@ -525,13 +557,28 @@ azdataQueryPlan.prototype.adjustGraphNodeHorizontalPositions = function (node) {
525557
Object.keys(levelsTable).map(key => {
526558
for (let levelNodeIndex = 1; levelNodeIndex < levelsTable[key].length; ++levelNodeIndex) {
527559
let previousNode = levelsTable[key][levelNodeIndex - 1];
528-
let currentNode = levelsTable[key][levelNodeIndex]
560+
let currentNode = levelsTable[key][levelNodeIndex];
529561

530562
let previousLabel = previousNode.label.split(/\r\n|\n/).filter(str => str.length > 20);
531563
if (previousLabel.length !== 0) {
564+
let longestString = '';
565+
let labelPartitions = currentNode.label.split(/\r\n|\n/g);
566+
labelPartitions.forEach(str => {
567+
if (longestString.length < str.length) {
568+
longestString = str;
569+
}
570+
});
571+
572+
var size = mxUtils.getSizeForString(longestString.substring(0, LABEL_LENGTH_LIMIT),
573+
mxConstants.DEFAULT_FONTSIZE,
574+
mxConstants.DEFAULT_FONTFAMILY,
575+
undefined,
576+
mxConstants.DEFAULT_FONTSTYLE);
577+
532578
let distanceFromPreviousNode = currentNode.position.x - previousNode.position.x;
579+
533580
if (distanceFromPreviousNode <= STANDARD_NODE_DISTANCE) {
534-
let shiftToRightAmount = IDEAL_LONG_LABEL_NODE_DISTANCE - distanceFromPreviousNode;
581+
let shiftToRightAmount = Math.max(size.width, IDEAL_LONG_LABEL_NODE_DISTANCE) - distanceFromPreviousNode;
535582
currentNode.position.x += shiftToRightAmount;
536583

537584
this.shiftParentAndChildNodePositionsHorizontally(currentNode.parent, shiftToRightAmount);
@@ -782,9 +829,6 @@ azdataQueryPlan.prototype.getPolygonPerimeter = function (cell) {
782829
return points;
783830
}
784831

785-
const NODE_HEIGHT = 100;
786-
const NODE_WIDTH = 100;
787-
788832
/**
789833
* Gets the left side points for the starting node in the polygon from top to bottom.
790834
* @param {*} cell The starting node for the left side perimeter points.
@@ -880,6 +924,9 @@ azdataQueryPlan.prototype.getRightSidePoints = function (cell) {
880924

881925
azdataQueryPlan.prototype.calcAdditionalSpacingForNode = function(cell) {
882926
let longestSubLabel = Math.max(...(cell.value.label.split(/\r\n|\n/).map(str => str.length)));
927+
if (longestSubLabel > LABEL_LENGTH_LIMIT) {
928+
longestSubLabel = LABEL_LENGTH_LIMIT;
929+
}
883930
// These values to work best for drawing regions around labels of different lengths, so the label is always inside the polygon.
884931
return longestSubLabel / 10 * 15;
885932
}

0 commit comments

Comments
 (0)