Skip to content

Commit 723f7c0

Browse files
committed
gui: when clicking the source link in the inspector open an editor
Uses the EDITOR envar substituting %FILE% and %LINE%, eg EDITOR="emacs +%LINE% %FILE%" make gui_final Related to #5189 Signed-off-by: Matt Liberty <[email protected]>
1 parent 8796891 commit 723f7c0

File tree

6 files changed

+155
-3
lines changed

6 files changed

+155
-3
lines changed

src/gui/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ if (Qt5_FOUND AND BUILD_GUI)
4747
src/gotoDialog.cpp
4848
src/inspector.cpp
4949
src/dbDescriptors.cpp
50+
src/fileLine.cpp
5051
src/staDescriptors.cpp
5152
src/highlightGroupDialog.cpp
5253
src/selectHighlightWindow.cpp

src/gui/src/dbDescriptors.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "bufferTreeDescriptor.h"
2929
#include "db_sta/dbNetwork.hh"
3030
#include "db_sta/dbSta.hh"
31+
#include "fileLine.h"
3132
#include "odb/db.h"
3233
#include "odb/dbShape.h"
3334
#include "odb/dbTransform.h"
@@ -91,9 +92,9 @@ static void populateODBProperties(Descriptor::Properties& props,
9192
const auto file_name_prop
9293
= odb::dbStringProperty::find(block, src_file.c_str());
9394
if (file_name_prop) {
94-
const auto info = fmt::format(
95-
"{}:{}", file_name_prop->getValue(), src_file_line.value());
96-
prop_list.emplace_back("Source", info);
95+
auto gui = Gui::get();
96+
FileLine file_line{file_name_prop->getValue(), src_file_line.value()};
97+
prop_list.emplace_back("Source", gui->makeSelected(file_line));
9798
}
9899
}
99100

src/gui/src/fileLine.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
// Copyright (c) 2025-2025, The OpenROAD Authors
3+
4+
#include "fileLine.h"
5+
6+
namespace gui {
7+
8+
std::string FileLineDescriptor::getName(const std::any& object) const
9+
{
10+
FileLine fl = std::any_cast<FileLine>(object);
11+
return fmt::format("{}:{}", fl.file_name, fl.line_number);
12+
}
13+
14+
std::string FileLineDescriptor::getTypeName() const
15+
{
16+
return "FileLine";
17+
}
18+
19+
bool FileLineDescriptor::getBBox(const std::any& object, odb::Rect& bbox) const
20+
{
21+
return false;
22+
}
23+
24+
void FileLineDescriptor::visitAllObjects(
25+
const std::function<void(const Selected&)>& func) const
26+
{
27+
}
28+
29+
Descriptor::Properties FileLineDescriptor::getProperties(
30+
const std::any& object) const
31+
{
32+
return {};
33+
}
34+
35+
Selected FileLineDescriptor::makeSelected(const std::any& object) const
36+
{
37+
FileLine fl = std::any_cast<FileLine>(object);
38+
return Selected(fl, this);
39+
}
40+
41+
bool FileLineDescriptor::lessThan(const std::any& l, const std::any& r) const
42+
{
43+
auto l_fl = std::any_cast<FileLine>(l);
44+
auto r_fl = std::any_cast<FileLine>(r);
45+
return std::tie(l_fl.file_name, l_fl.line_number)
46+
< std::tie(r_fl.file_name, r_fl.line_number);
47+
}
48+
49+
void FileLineDescriptor::highlight(const std::any& object,
50+
Painter& painter) const
51+
{
52+
}
53+
54+
} // namespace gui

src/gui/src/fileLine.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
// Copyright (c) 2025-2025, The OpenROAD Authors
3+
4+
#pragma once
5+
6+
#include "gui/gui.h"
7+
8+
namespace gui {
9+
10+
// Hold reference to a source file + line number (eg Verilog source)
11+
struct FileLine
12+
{
13+
const std::string file_name;
14+
const int line_number;
15+
};
16+
17+
// Something of a placeholder as we don't intend to show these in
18+
// the inspector but just trigger a file open. However Selected
19+
// expects a descriptor and it is safer to just provide one.
20+
class FileLineDescriptor : public Descriptor
21+
{
22+
public:
23+
std::string getName(const std::any& object) const override;
24+
std::string getTypeName() const override;
25+
bool getBBox(const std::any& object, odb::Rect& bbox) const override;
26+
27+
void visitAllObjects(
28+
const std::function<void(const Selected&)>& func) const override;
29+
30+
Properties getProperties(const std::any& object) const override;
31+
32+
Selected makeSelected(const std::any& object) const override;
33+
34+
bool lessThan(const std::any& l, const std::any& r) const override;
35+
36+
void highlight(const std::any& object, Painter& painter) const override;
37+
};
38+
39+
} // namespace gui

src/gui/src/inspector.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
#include <QDebug>
99
#include <QHeaderView>
1010
#include <QLineEdit>
11+
#include <QProcess>
12+
#include <QProcessEnvironment>
1113
#include <QPushButton>
14+
#include <QStringList>
1215
#include <algorithm>
1316
#include <any>
1417
#include <cmath>
@@ -20,6 +23,7 @@
2023
#include <utility>
2124
#include <vector>
2225

26+
#include "fileLine.h"
2327
#include "gui/gui.h"
2428
#include "gui_utils.h"
2529

@@ -964,13 +968,63 @@ void Inspector::doubleClicked(const QModelIndex& index)
964968
emit indexDoubleClicked(index);
965969
}
966970

971+
bool invokeEditorFromEnv(const FileLine& file_line)
972+
{
973+
QString editorCommand
974+
= QProcessEnvironment::systemEnvironment().value("EDITOR");
975+
976+
if (editorCommand.isEmpty()) {
977+
qWarning() << "EDITOR environment variable not set.";
978+
return false;
979+
}
980+
981+
// Check if the placeholders are present in the command string.
982+
if (!editorCommand.contains("%FILE%") || !editorCommand.contains("%LINE%")) {
983+
qWarning() << "Error: The EDITOR command does not contain the necessary "
984+
"placeholders.";
985+
qWarning()
986+
<< "It must contain both %FILE% and %LINE% to function correctly.";
987+
qWarning() << "Current command:" << editorCommand;
988+
return false;
989+
}
990+
991+
// Substitute placeholders.
992+
editorCommand.replace("%FILE%", QString::fromStdString(file_line.file_name));
993+
editorCommand.replace("%LINE%", QString::number(file_line.line_number));
994+
995+
QStringList arguments;
996+
QString program;
997+
998+
// Split the command into the program name and arguments.
999+
// The first token is the program itself.
1000+
QRegExp rx("(\\ |\\t)");
1001+
QStringList parts = editorCommand.split(rx, Qt::SkipEmptyParts);
1002+
if (!parts.isEmpty()) {
1003+
program = parts.first();
1004+
arguments = parts.mid(1);
1005+
} else {
1006+
qWarning() << "Invalid EDITOR command:" << editorCommand;
1007+
return false;
1008+
}
1009+
1010+
// 4. Execute the command.
1011+
QProcess::startDetached(program, arguments);
1012+
return true;
1013+
}
1014+
9671015
void Inspector::indexClicked()
9681016
{
9691017
// handle single click event
9701018
QStandardItem* item = model_->itemFromIndex(clicked_index_);
9711019
auto new_selected
9721020
= item->data(EditorItemDelegate::kSelected).value<Selected>();
9731021
if (new_selected) {
1022+
if (const FileLine* v
1023+
= std::any_cast<const FileLine>(&new_selected.getObject())) {
1024+
if (invokeEditorFromEnv(*v)) {
1025+
return;
1026+
}
1027+
}
9741028
if (navigation_history_.empty()) {
9751029
// add starting object
9761030
navigation_history_.push_back(selection_);

src/gui/src/mainWindow.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "dbDescriptors.h"
4343
#include "displayControls.h"
4444
#include "drcWidget.h"
45+
#include "fileLine.h"
4546
#include "globalConnectDialog.h"
4647
#include "gui/heatMap.h"
4748
#include "helpWidget.h"
@@ -620,6 +621,8 @@ void MainWindow::init(sta::dbSta* sta, const std::string& help_path)
620621
gui->registerDescriptor<sta::Instance*>(new StaInstanceDescriptor(sta));
621622
gui->registerDescriptor<sta::Clock*>(new ClockDescriptor(sta));
622623

624+
gui->registerDescriptor<FileLine>(new FileLineDescriptor);
625+
623626
gui->registerDescriptor<BufferTree>(
624627
new BufferTreeDescriptor(db_,
625628
sta,

0 commit comments

Comments
 (0)