Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 6309e7e

Browse filesBrowse files
add hyperlinks to source for failed expectations, errors, warnings, info
1 parent dd2754e commit 6309e7e
Copy full SHA for 6309e7e

File tree

1 file changed

+119
-39
lines changed
Filter options

1 file changed

+119
-39
lines changed

‎sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerPanel.xtend

Copy file name to clipboardExpand all lines: sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerPanel.xtend
+119-39Lines changed: 119 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import java.awt.event.MouseEvent
2828
import java.awt.event.MouseListener
2929
import java.text.DecimalFormat
3030
import java.util.ArrayList
31+
import java.util.regex.Pattern
3132
import javax.swing.Box
3233
import javax.swing.DefaultComboBoxModel
3334
import javax.swing.JCheckBoxMenuItem
@@ -46,6 +47,8 @@ import javax.swing.JTable
4647
import javax.swing.SwingConstants
4748
import javax.swing.UIManager
4849
import javax.swing.border.EmptyBorder
50+
import javax.swing.event.HyperlinkEvent
51+
import javax.swing.event.HyperlinkListener
4952
import javax.swing.event.ListSelectionEvent
5053
import javax.swing.event.ListSelectionListener
5154
import javax.swing.plaf.basic.BasicProgressBarUI
@@ -63,7 +66,7 @@ import org.utplsql.sqldev.resources.UtplsqlResources
6366
import org.utplsql.sqldev.runner.UtplsqlRunner
6467
import org.utplsql.sqldev.runner.UtplsqlWorksheetRunner
6568

66-
class RunnerPanel implements ActionListener, MouseListener {
69+
class RunnerPanel implements ActionListener, MouseListener, HyperlinkListener {
6770
static val GREEN = new Color(0, 153, 0)
6871
static val RED = new Color(153, 0, 0)
6972
static val INDICATOR_WIDTH = 20
@@ -103,10 +106,10 @@ class RunnerPanel implements ActionListener, MouseListener {
103106
RunnerTextField testStartTextField
104107
FailuresTableModel failuresTableModel
105108
JTable failuresTable
106-
RunnerTextArea testFailureMessageTextArea
107-
RunnerTextArea testErrorStackTextArea
108-
RunnerTextArea testWarningsTextArea
109-
RunnerTextArea testServerOutputTextArea
109+
RunnerTextPane testFailureMessageTextPane
110+
RunnerTextPane testErrorStackTextPane
111+
RunnerTextPane testWarningsTextPane
112+
RunnerTextPane testServerOutputTextPane
110113
JTabbedPane testDetailTabbedPane
111114

112115
def Component getGUI() {
@@ -131,10 +134,10 @@ class RunnerPanel implements ActionListener, MouseListener {
131134
testStartTextField.text = null
132135
failuresTableModel.model = null
133136
failuresTableModel.fireTableDataChanged
134-
testFailureMessageTextArea.text = null
135-
testErrorStackTextArea.text = null
136-
testWarningsTextArea.text = null
137-
testServerOutputTextArea.text = null
137+
testFailureMessageTextPane.text = null
138+
testErrorStackTextPane.text = null
139+
testWarningsTextPane.text = null
140+
testServerOutputTextPane.text = null
138141
}
139142

140143
private def refreshRunsComboBox() {
@@ -216,6 +219,39 @@ class RunnerPanel implements ActionListener, MouseListener {
216219
}
217220
}
218221

222+
private def getHtml(String text) {
223+
val html = '''
224+
<html>
225+
<head>
226+
<style type="text/css">
227+
body, p {font-family: «testOwnerTextField.font.family»; font-size: 1.0em; line-height: 1.1em; margin-top: 0px; margin-bottom: 0px;}
228+
</style>
229+
</head>
230+
<body>
231+
«getLinkedText(text)»
232+
</body>
233+
</html>
234+
'''
235+
return html
236+
}
237+
238+
private def openLink(String link) {
239+
val parts = link.split("/")
240+
val ownerName = parts.get(0)
241+
val objectName = parts.get(1)
242+
var line = Integer.parseInt(parts.get(2))
243+
val dao = new UtplsqlDao(Connections.instance.getConnection(currentRun.connectionName))
244+
val objectType = dao.getObjectType(ownerName, objectName)
245+
val fixedObjectType = '''«objectType»«IF objectType == "PACKAGE" || objectType == "TYPE"» BODY«ENDIF»'''
246+
if (parts.size == 4) {
247+
val procedureName = parts.get(3)
248+
val source = dao.getSource(ownerName, fixedObjectType, objectName).trim
249+
val parser = new UtplsqlParser(source)
250+
line = parser.getLineOf(procedureName)
251+
}
252+
openEditor(ownerName, '''«objectType»«IF objectType == "PACKAGE" || objectType == "TYPE"» BODY«ENDIF»''', objectName.toUpperCase, line, 1)
253+
}
254+
219255
private def openEditor(String owner, String type, String name, int line, int col) {
220256
var drillLink = new DefaultDrillLink
221257
drillLink.connName = currentRun.connectionName
@@ -442,6 +478,13 @@ class RunnerPanel implements ActionListener, MouseListener {
442478
override mouseReleased(MouseEvent e) {
443479
}
444480

481+
override hyperlinkUpdate(HyperlinkEvent e) {
482+
if (e.eventType == HyperlinkEvent.EventType.ACTIVATED) {
483+
val link = e.description
484+
openLink(link)
485+
}
486+
}
487+
445488
private static def formatDateTime(String dateTime) {
446489
if (dateTime === null) {
447490
return null
@@ -474,19 +517,53 @@ class RunnerPanel implements ActionListener, MouseListener {
474517
p.testStartTextField.text = formatDateTime(test.startTime)
475518
p.failuresTableModel.model = test.failedExpectations
476519
p.failuresTableModel.fireTableDataChanged
477-
p.testFailureMessageTextArea.text = null
520+
p.testFailureMessageTextPane.text = null
478521
if (test.failedExpectations !== null && test.failedExpectations.size > 0) {
479522
p.failuresTable.setRowSelectionInterval(0, 0)
480523
}
481-
p.testErrorStackTextArea.text = test.errorStack?.trim
482-
p.testWarningsTextArea.text = test.warnings?.trim
483-
p.testServerOutputTextArea.text = test.serverOutput?.trim
524+
p.testErrorStackTextPane.text = p.getHtml(test.errorStack?.trim)
525+
p.testWarningsTextPane.text = p.getHtml(test.warnings?.trim)
526+
p.testServerOutputTextPane.text = p.getHtml(test.serverOutput?.trim)
484527
p.syncDetailTab
485528
p.testOverviewRunMenuItem.enabled = true
486529
p.testOverviewRunWorksheetMenuItem.enabled = true
487530
}
488531
}
489532
}
533+
534+
private def getLinkedText(String text) {
535+
if (text === null) {
536+
return ""
537+
}
538+
// Patterns (primarily Asserts, Errors, ServerOutput):
539+
// at "OWNER.PACKAGE.PROCEDURE", line 42
540+
// at "OWNER.PROCEDURE", line 42
541+
val p1 = Pattern.compile('''\s+("([^\.]+)\.([^\."]+)(?:\.([^\"]+))?",\s+line\s+([0-9]+))''')
542+
var localText = text
543+
var m = p1.matcher(localText)
544+
while(m.find) {
545+
val link = ''' <a href="«m.group(2)»/«m.group(3)»/«m.group(5)»">«m.group(1)»</a>'''
546+
localText = localText.replaceFirst(p1.pattern, link)
547+
m = p1.matcher(localText)
548+
}
549+
// Patterns (primarily Warnings, without line reference, calculate when opening link):
550+
// owner.package.procedure
551+
val p2 = Pattern.compile('''^\s{2}(([^\.]+)\.([^\.]+)\.(.+))$''', Pattern.MULTILINE)
552+
m = p2.matcher(localText)
553+
while(m.find) {
554+
val link = '''&nbsp;&nbsp;<a href="«m.group(2).toUpperCase»/«m.group(3).toUpperCase»/1/«m.group(4).toUpperCase»">«m.group(1)»</a>'''
555+
val start = m.start(0)
556+
val end = m.end(0)
557+
localText = '''«localText.substring(0, start)»«link»«localText.substring(end)»'''
558+
m = p2.matcher(localText)
559+
}
560+
val result = '''
561+
«FOR p : localText.split(System.lineSeparator)»
562+
<p>«p»</p>
563+
«ENDFOR»
564+
'''
565+
return result
566+
}
490567

491568
static class FailuresRowListener implements ListSelectionListener {
492569
RunnerPanel p
@@ -500,7 +577,9 @@ class RunnerPanel implements ActionListener, MouseListener {
500577
if (rowIndex != -1) {
501578
val row = p.failuresTable.convertRowIndexToModel(rowIndex)
502579
val expectation = p.failuresTableModel.getExpectation(row)
503-
p.testFailureMessageTextArea.text = expectation.failureText
580+
val html = p.getHtml(expectation.failureText)
581+
p.testFailureMessageTextPane.text = html
582+
504583
}
505584
}
506585
}
@@ -958,12 +1037,12 @@ class RunnerPanel implements ActionListener, MouseListener {
9581037
failuresDescription.headerRenderer = failuresTableHeaderRenderer
9591038
val failuresTableScrollPane = new JScrollPane(failuresTable)
9601039
// - failures details
961-
testFailureMessageTextArea = new RunnerTextArea
962-
testFailureMessageTextArea.editable = false
963-
testFailureMessageTextArea.enabled = true
964-
testFailureMessageTextArea.lineWrap = true
965-
testFailureMessageTextArea.wrapStyleWord = true
966-
val testFailureMessageScrollPane = new JScrollPane(testFailureMessageTextArea)
1040+
testFailureMessageTextPane = new RunnerTextPane
1041+
testFailureMessageTextPane.editable = false
1042+
testFailureMessageTextPane.enabled = true
1043+
testFailureMessageTextPane.contentType = "text/html"
1044+
testFailureMessageTextPane.addHyperlinkListener(this)
1045+
val testFailureMessageScrollPane = new JScrollPane(testFailureMessageTextPane)
9671046
c.gridx = 1
9681047
c.gridy = 0
9691048
c.gridwidth = 1
@@ -981,12 +1060,12 @@ class RunnerPanel implements ActionListener, MouseListener {
9811060
// Errors tabbed pane (Error Stack)
9821061
val testErrorStackPanel = new JPanel
9831062
testErrorStackPanel.setLayout(new GridBagLayout())
984-
testErrorStackTextArea = new RunnerTextArea
985-
testErrorStackTextArea.editable = false
986-
testErrorStackTextArea.enabled = true
987-
testErrorStackTextArea.lineWrap = true
988-
testErrorStackTextArea.wrapStyleWord = true
989-
val testErrorStackScrollPane = new JScrollPane(testErrorStackTextArea)
1063+
testErrorStackTextPane = new RunnerTextPane
1064+
testErrorStackTextPane.editable = false
1065+
testErrorStackTextPane.enabled = true
1066+
testErrorStackTextPane.contentType = "text/html"
1067+
testErrorStackTextPane.addHyperlinkListener(this)
1068+
val testErrorStackScrollPane = new JScrollPane(testErrorStackTextPane)
9901069
c.gridx = 0
9911070
c.gridy = 0
9921071
c.gridwidth = 1
@@ -1001,12 +1080,12 @@ class RunnerPanel implements ActionListener, MouseListener {
10011080
// Warnings tabbed pane
10021081
val testWarningsPanel = new JPanel
10031082
testWarningsPanel.setLayout(new GridBagLayout())
1004-
testWarningsTextArea = new RunnerTextArea
1005-
testWarningsTextArea.editable = false
1006-
testWarningsTextArea.enabled = true
1007-
testWarningsTextArea.lineWrap = true
1008-
testWarningsTextArea.wrapStyleWord = true
1009-
val testWarningsScrollPane = new JScrollPane(testWarningsTextArea)
1083+
testWarningsTextPane = new RunnerTextPane
1084+
testWarningsTextPane.editable = false
1085+
testWarningsTextPane.enabled = true
1086+
testWarningsTextPane.contentType = "text/html"
1087+
testWarningsTextPane.addHyperlinkListener(this)
1088+
val testWarningsScrollPane = new JScrollPane(testWarningsTextPane)
10101089
c.gridx = 0
10111090
c.gridy = 0
10121091
c.gridwidth = 1
@@ -1021,12 +1100,12 @@ class RunnerPanel implements ActionListener, MouseListener {
10211100
// Info tabbed pane (Server Output)
10221101
val testServerOutputPanel = new JPanel
10231102
testServerOutputPanel.setLayout(new GridBagLayout())
1024-
testServerOutputTextArea = new RunnerTextArea
1025-
testServerOutputTextArea.editable = false
1026-
testServerOutputTextArea.enabled = true
1027-
testServerOutputTextArea.lineWrap = true
1028-
testServerOutputTextArea.wrapStyleWord = true
1029-
val testServerOutputScrollPane = new JScrollPane(testServerOutputTextArea)
1103+
testServerOutputTextPane = new RunnerTextPane
1104+
testServerOutputTextPane.editable = false
1105+
testServerOutputTextPane.enabled = true
1106+
testServerOutputTextPane.contentType = "text/html"
1107+
testServerOutputTextPane.addHyperlinkListener(this)
1108+
val testServerOutputScrollPane = new JScrollPane(testServerOutputTextPane)
10301109
c.gridx = 0
10311110
c.gridy = 0
10321111
c.gridwidth = 1
@@ -1062,5 +1141,6 @@ class RunnerPanel implements ActionListener, MouseListener {
10621141
val referenceBorder = testOwnerTextField.border
10631142
testDescriptionTextArea.border = referenceBorder
10641143
testIdTextArea.border = referenceBorder
1065-
}
1144+
}
1145+
10661146
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.