Skip to content
This repository was archived by the owner on Apr 6, 2024. It is now read-only.

Commit 59eb1be

Browse files
authored
Merge pull request #54 from cake-contrib/feature/file-menu
(GH-49) Add IDE integration option
2 parents 6c343f8 + 685ecd6 commit 59eb1be

File tree

6 files changed

+307
-20
lines changed

6 files changed

+307
-20
lines changed

src/Cake.Issues.Reporting.Generic.Tests/Cake.Issues.Reporting.Generic.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
<Compile Include="ColumnSortOrderExtensionsTests.cs" />
8787
<Compile Include="DevExtremeThemeExtensionsTests.cs" />
8888
<Compile Include="ExceptionAssertExtensions.cs" />
89+
<Compile Include="IdeIntegrationSettingsTests.cs" />
8990
<Compile Include="HtmlDxDataGridColumnDescriptionTests.cs" />
9091
<Compile Include="IIssueExtensionsTests.cs" />
9192
<Compile Include="UriExtensionsTests.cs" />
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
namespace Cake.Issues.Reporting.Generic.Tests
2+
{
3+
using System;
4+
using Cake.Issues.Testing;
5+
using Shouldly;
6+
using Xunit;
7+
8+
public sealed class IdeIntegrationSettingsTests
9+
{
10+
public sealed class TheGetOpenInIdeCallMethod
11+
{
12+
[Fact]
13+
public void Should_Throw_If_FilePathExpression_Is_Null()
14+
{
15+
// Given
16+
var ideIntegrationSettings = new IdeIntegrationSettings();
17+
string filePathExpression = null;
18+
var lineExpression = "line";
19+
20+
// When
21+
var result = Record.Exception(() =>
22+
ideIntegrationSettings.GetOpenInIdeCall(filePathExpression, lineExpression));
23+
24+
// Then
25+
result.IsArgumentNullException("filePathExpression");
26+
}
27+
28+
[Fact]
29+
public void Should_Throw_If_FilePathExpression_Is_Empty()
30+
{
31+
// Given
32+
var ideIntegrationSettings = new IdeIntegrationSettings();
33+
var filePathExpression = string.Empty;
34+
var lineExpression = "line";
35+
36+
// When
37+
var result = Record.Exception(() =>
38+
ideIntegrationSettings.GetOpenInIdeCall(filePathExpression, lineExpression));
39+
40+
// Then
41+
result.IsArgumentOutOfRangeException("filePathExpression");
42+
}
43+
44+
[Fact]
45+
public void Should_Throw_If_FilePathExpression_Is_WhiteSpace()
46+
{
47+
// Given
48+
var ideIntegrationSettings = new IdeIntegrationSettings();
49+
var filePathExpression = " ";
50+
var lineExpression = "line";
51+
52+
// When
53+
var result = Record.Exception(() =>
54+
ideIntegrationSettings.GetOpenInIdeCall(filePathExpression, lineExpression));
55+
56+
// Then
57+
result.IsArgumentOutOfRangeException("filePathExpression");
58+
}
59+
60+
[Fact]
61+
public void Should_Throw_If_LineExpression_Is_Null()
62+
{
63+
// Given
64+
var ideIntegrationSettings = new IdeIntegrationSettings();
65+
var filePathExpression = "file";
66+
string lineExpression = null;
67+
68+
// When
69+
var result = Record.Exception(() =>
70+
ideIntegrationSettings.GetOpenInIdeCall(filePathExpression, lineExpression));
71+
72+
// Then
73+
result.IsArgumentNullException("lineExpression");
74+
}
75+
76+
[Fact]
77+
public void Should_Throw_If_LineExpression_Is_Empty()
78+
{
79+
// Given
80+
var ideIntegrationSettings = new IdeIntegrationSettings();
81+
var filePathExpression = "file";
82+
var lineExpression = string.Empty;
83+
84+
// When
85+
var result = Record.Exception(() =>
86+
ideIntegrationSettings.GetOpenInIdeCall(filePathExpression, lineExpression));
87+
88+
// Then
89+
result.IsArgumentOutOfRangeException("lineExpression");
90+
}
91+
92+
[Fact]
93+
public void Should_Throw_If_LineExpression_Is_WhiteSpace()
94+
{
95+
// Given
96+
var ideIntegrationSettings = new IdeIntegrationSettings();
97+
var filePathExpression = "file";
98+
var lineExpression = " ";
99+
100+
// When
101+
var result = Record.Exception(() =>
102+
ideIntegrationSettings.GetOpenInIdeCall(filePathExpression, lineExpression));
103+
104+
// Then
105+
result.IsArgumentOutOfRangeException("lineExpression");
106+
}
107+
108+
[Fact]
109+
public void Should_Return_Null_If_OpenInIdeCall_Is_Not_Set()
110+
{
111+
// Given
112+
var ideIntegrationSettings = new IdeIntegrationSettings();
113+
var filePathExpression = "file";
114+
var lineExpression = "line";
115+
116+
// When
117+
var result = ideIntegrationSettings.GetOpenInIdeCall(filePathExpression, lineExpression);
118+
119+
// Then
120+
result.ShouldBeNull();
121+
}
122+
123+
[Fact]
124+
public void Should_Replace_FilePath_Token()
125+
{
126+
// Given
127+
var ideIntegrationSettings =
128+
new IdeIntegrationSettings
129+
{
130+
OpenInIdeCall = "Foo{FilePath}Bar"
131+
};
132+
var filePathExpression = "file";
133+
var lineExpression = "line";
134+
135+
// When
136+
var result = ideIntegrationSettings.GetOpenInIdeCall(filePathExpression, lineExpression);
137+
138+
// Then
139+
result.ShouldBe("FoofileBar");
140+
}
141+
142+
[Fact]
143+
public void Should_Replace_Line_Token()
144+
{
145+
// Given
146+
var ideIntegrationSettings =
147+
new IdeIntegrationSettings
148+
{
149+
OpenInIdeCall = "Foo{Line}Bar"
150+
};
151+
var filePathExpression = "file";
152+
var lineExpression = "line";
153+
154+
// When
155+
var result = ideIntegrationSettings.GetOpenInIdeCall(filePathExpression, lineExpression);
156+
157+
// Then
158+
result.ShouldBe("FoolineBar");
159+
}
160+
}
161+
}
162+
}

src/Cake.Issues.Reporting.Generic/Cake.Issues.Reporting.Generic.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
<Compile Include="GenericIssueReportFormatSettingsExtensions.cs" />
7979
<Compile Include="GenericIssueReportGenerator.cs" />
8080
<Compile Include="GenericIssueReportFormatSettings.cs" />
81+
<Compile Include="IdeIntegrationSettings.cs" />
8182
<Compile Include="IIssueExtensions.cs" />
8283
<Compile Include="ReportColumn.cs" />
8384
<Compile Include="UriExtensions.cs" />

src/Cake.Issues.Reporting.Generic/HtmlDxDataGridOption.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,13 @@ public enum HtmlDxDataGridOption
300300
/// If setting this the matching <see cref="JQueryVersion"/> needs to also be set.
301301
/// Default value is <c>18.2.7</c>.
302302
/// </summary>
303-
DevExtremeVersion
303+
DevExtremeVersion,
304+
305+
/// <summary>
306+
/// Settings for having functionality to open files affected by issues in IDEs.
307+
/// Value needs to be an instance of <see cref="IdeIntegrationSettings"/>.
308+
/// Default value is <c>null</c>.
309+
/// </summary>
310+
IdeIntegrationSettings
304311
}
305312
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
namespace Cake.Issues.Reporting.Generic
2+
{
3+
/// <summary>
4+
/// Settings how issues should be integrated to IDEs.
5+
/// </summary>
6+
public class IdeIntegrationSettings
7+
{
8+
/// <summary>
9+
/// Gets or sets additional JavaScript which should be added.
10+
/// </summary>
11+
public string JavaScript { get; set; }
12+
13+
/// <summary>
14+
/// Gets or sets JavaScript which should be called to open the file affected by an issue in an IDE.
15+
/// </summary>
16+
public string OpenInIdeCall { get; set; }
17+
18+
/// <summary>
19+
/// Gets or sets text which should be shown in the drop down menu for opening the file affected
20+
/// by an issue in an IDE.
21+
/// Default value is <c>Open in IDE</c>.
22+
/// </summary>
23+
public string MenuEntryText { get; set; } = "Open in IDE";
24+
25+
/// <summary>
26+
/// Returns the JavaScript which should be called to open the file affected by an issue in an IDE
27+
/// with all patterns of <see cref="OpenInIdeCall"/> replaced.
28+
/// </summary>
29+
/// <param name="filePathExpression">Expression which should be used to get the path and name
30+
/// of the file at runtime.</param>
31+
/// <param name="lineExpression">Expression which should be used to get the line number at runtime.</param>
32+
/// <returns>JavaScript which should be called to open the file affected by an issue in an IDE
33+
/// with all patterns replaced.</returns>
34+
public string GetOpenInIdeCall(string filePathExpression, string lineExpression)
35+
{
36+
filePathExpression.NotNullOrWhiteSpace(nameof(filePathExpression));
37+
lineExpression.NotNullOrWhiteSpace(nameof(lineExpression));
38+
39+
if (string.IsNullOrWhiteSpace(this.OpenInIdeCall))
40+
{
41+
return null;
42+
}
43+
44+
return
45+
this.OpenInIdeCall
46+
.Replace("{FilePath}", filePathExpression)
47+
.Replace("{Line}", lineExpression);
48+
}
49+
}
50+
}

src/Cake.Issues.Reporting.Generic/Templates/DxDataGrid.cshtml

Lines changed: 85 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
var groupedColumns = ViewBagHelper.ValueOrDefault(ViewBag.GroupedColumns, new List<ReportColumn> { ReportColumn.ProviderName });
4343
var sortedColumns = ViewBagHelper.ValueOrDefault(ViewBag.SortedColumns, new List<ReportColumn> { ReportColumn.PriorityName, ReportColumn.ProjectName, ReportColumn.FileDirectory, ReportColumn.FileName, ReportColumn.Line });
4444
FileLinkSettings fileLinkSettings = ViewBagHelper.ValueOrDefault(ViewBag.FileLinkSettings, new FileLinkSettings());
45+
IdeIntegrationSettings ideIntegrationSettings = ViewBagHelper.ValueOrDefault<IdeIntegrationSettings>(ViewBag.IdeIntegrationSettings, null);
4546
List<HtmlDxDataGridColumnDescription> additionalColumns = ViewBagHelper.ValueOrDefault(ViewBag.AdditionalColumns, new List<HtmlDxDataGridColumnDescription>());
4647
string jQueryLocation = ViewBagHelper.ValueOrDefault(ViewBag.JQueryLocation, "https://ajax.aspnetcdn.com/ajax/jquery/");
4748
string jQueryVersion = ViewBagHelper.ValueOrDefault(ViewBag.JQueryVersion, "3.1.0");
@@ -61,10 +62,10 @@
6162
addPriorityName: priorityNameVisible,
6263
addProjectPath: projectPathVisible,
6364
addProjectName: projectNameVisible,
64-
addFilePath: filePathVisible,
65+
addFilePath: filePathVisible || ideIntegrationSettings != null,
6566
addFileDirectory: fileDirectoryVisible,
6667
addFileName: fileNameVisible,
67-
addLine: lineVisible,
68+
addLine: lineVisible || ideIntegrationSettings != null,
6869
addRule: ruleVisible,
6970
addRuleUrl: ruleVisible || ruleUrlVisible,
7071
addMessage: messageVisible,
@@ -94,6 +95,30 @@
9495
<link rel="stylesheet" type="text/css" href="@(devExtremeLocation)@(devExtremeVersion)/css/@(theme.GetCssFileName())" />
9596
@* DevExtreme library *@
9697
<script type="text/javascript" src="@(devExtremeLocation)@(devExtremeVersion)/js/dx.all.js"></script>
98+
@* Additional JavaScript for IDE integration *@
99+
@if (ideIntegrationSettings != null && !string.IsNullOrWhiteSpace(ideIntegrationSettings.JavaScript))
100+
{
101+
<script type="text/javascript">
102+
@Raw(ideIntegrationSettings.JavaScript)
103+
</script>
104+
}
105+
106+
<style>
107+
@* Styles for making sure drop down glyph is not shown for menu in file column in any theme *@
108+
td[role=gridcell] .dx-menu-item-popout
109+
{
110+
display: none;
111+
}
112+
td[role=gridcell] .dx-icon
113+
{
114+
margin: 0px !important;
115+
}
116+
@* Style for making sure menu in file column in any theme is not too high *@
117+
.dx-datagrid .dx-menu .dx-menu-item .dx-menu-item-content, .dx-datagrid-container .dx-menu .dx-menu-item .dx-menu-item-content
118+
{
119+
padding: 0px;
120+
}
121+
</style>
97122
</head>
98123
<body class="dx-viewport">
99124
@if (showHeader)
@@ -110,7 +135,37 @@
110135
</script>
111136

112137
<script type="text/javascript">
113-
$(function(){
138+
$(function () {
139+
@if (ideIntegrationSettings != null && !string.IsNullOrWhiteSpace(ideIntegrationSettings.OpenInIdeCall))
140+
{
141+
<text>
142+
@* Creates the menu in the file column *@
143+
function getFileCellMenuElement(filePath, line) {
144+
var element =
145+
$('<div>')
146+
.css("float", "right")
147+
.dxMenu({
148+
items: [{
149+
text: "",
150+
icon: "overflow",
151+
items: [
152+
{
153+
text: "@ideIntegrationSettings.MenuEntryText",
154+
action: "openInIde"
155+
}
156+
]
157+
}],
158+
onItemClick: function (e) {
159+
if (e.itemData.action === "openInIde") {
160+
@Raw(ideIntegrationSettings.GetOpenInIdeCall("filePath", "line"))
161+
}
162+
}
163+
});
164+
return element;
165+
}
166+
</text>
167+
};
168+
114169
$("#gridContainer").dxDataGrid({
115170
dataSource: issues,
116171
loadPanel: {
@@ -295,22 +350,33 @@
295350
@:sortIndex: @sortedColumns.IndexOf(ReportColumn.FileName),
296351
@:sortOrder: "@fileNameSortOrder.ToShortString()",
297352
}
298-
@if (fileLinkSettings != null && !string.IsNullOrWhiteSpace(fileLinkSettings.FileLinkPattern))
299-
{
300-
<text>
301-
cellTemplate: function (container, options) {
302-
if (options.data["FileLink"]) {
303-
$('<a>', {
304-
text: options.value,
305-
href: options.data["FileLink"],
306-
target: "_blank"
307-
}).appendTo(container);
308-
}
309-
else {
310-
container.text(options.value);
311-
}
312-
}
313-
</text>
353+
cellTemplate: function (container, options) {
354+
if (options.data["FileLink"]) {
355+
var $wrapper =
356+
$('<div>')
357+
.css("float", "left");
358+
var $link =
359+
$('<a>', {
360+
text: options.value,
361+
href: options.data["FileLink"],
362+
target: "_blank"
363+
});
364+
$wrapper.append($link);
365+
$wrapper.appendTo(container);
366+
}
367+
else {
368+
$('<div>')
369+
.text(options.value)
370+
.css("float", "left")
371+
.appendTo(container);
372+
}
373+
@if (ideIntegrationSettings != null && !string.IsNullOrWhiteSpace(ideIntegrationSettings.OpenInIdeCall))
374+
{
375+
<text>
376+
getFileCellMenuElement(options.data["FilePath"], options.data["Line"])
377+
.appendTo(container);
378+
</text>
379+
}
314380
}
315381
},
316382
</text>

0 commit comments

Comments
 (0)