Skip to content

Commit 9c81589

Browse files
committed
Code finder
1 parent 72167c5 commit 9c81589

4 files changed

Lines changed: 133 additions & 0 deletions

File tree

iommi/apps.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from django.apps import AppConfig
22

3+
from iommi.code_finder import setup_code_finder
34
from iommi.from_model import register_search_fields
45
from iommi.style_bootstrap_docs import bootstrap_docs
56

@@ -56,3 +57,5 @@ def ready(self):
5657

5758
register_factory(GenericRelation, factory=None)
5859
register_factory(GenericForeignKey, factory=None)
60+
61+
setup_code_finder()

iommi/code_finder.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from pathlib import Path
2+
3+
from django.conf import settings
4+
from django.template.loader_tags import IncludeNode
5+
6+
from iommi._web_compat import format_html
7+
from iommi.debug import src_debug_url_builder
8+
9+
10+
def setup_code_finder():
11+
if not settings.DEBUG:
12+
return
13+
14+
# IncludeNode monkey patch - output link to template location before each included template
15+
orig_include_render = IncludeNode.render
16+
17+
def include_render(self, context):
18+
# Get the file path and line number from the node's origin and token
19+
link_html = ''
20+
if hasattr(self, 'origin') and self.origin:
21+
filename = self.origin.name
22+
# Token has a lineno attribute that gives us the line number
23+
line_number = self.token.lineno if hasattr(self, 'token') and hasattr(self.token, 'lineno') else 1
24+
25+
# Create a PyCharm link
26+
link_html = format_html(
27+
'<!-- ## iommi-code-finder-URL ## {} ## {} -->',
28+
f'{Path(filename).name}:{line_number}',
29+
src_debug_url_builder(filename, line_number)
30+
)
31+
32+
result = link_html + orig_include_render(self, context)
33+
return result
34+
35+
IncludeNode.render = include_render

iommi/menu.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,11 @@ class DebugMenu(Menu):
307307
attrs__onclick='window.iommi_start_pick()',
308308
tag='li',
309309
)
310+
code_finder = MenuItem(
311+
url='#',
312+
attrs__onclick='window.insert_iommi_code_finder_links()',
313+
tag='li',
314+
)
310315
edit = MenuItem(
311316
display_name=lambda request, **_: (
312317
'Edit vertical'

iommi/static/js/iommi.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,3 +611,93 @@ document.addEventListener('readystatechange', () => {
611611
window.iommi.select2.onCompleteReadyState();
612612
}
613613
});
614+
615+
616+
function insert_iommi_code_finder_links() {
617+
remove_iommi_code_finder_links();
618+
619+
// Find all matching comments
620+
const walker = document.createTreeWalker(
621+
document.documentElement,
622+
NodeFilter.SHOW_COMMENT,
623+
{
624+
acceptNode: function (node) {
625+
return node.textContent.includes('## iommi-code-finder-URL ##')
626+
? NodeFilter.FILTER_ACCEPT
627+
: NodeFilter.FILTER_SKIP;
628+
}
629+
},
630+
false
631+
);
632+
633+
const comments = [];
634+
let comment;
635+
while (comment = walker.nextNode()) {
636+
comments.push(comment);
637+
}
638+
639+
// Process each comment
640+
comments.forEach(comment => {
641+
// Extract content between the markers
642+
// Format: "## iommi-code-finder-URL ## short name ## https://example.com/path"
643+
const match = comment.textContent.match(/## iommi-code-finder-URL ##\s*(.+?)\s*##\s*(.+?)$/);
644+
if (!match || !match[1] || !match[2]) return;
645+
646+
const shortName = match[1].trim();
647+
const url = match[2].trim();
648+
649+
// Find the next element node after the comment
650+
let nextNode = comment.nextSibling;
651+
while (nextNode && nextNode.nodeType !== 1) {
652+
nextNode = nextNode.nextSibling;
653+
}
654+
655+
if (!nextNode) {
656+
// No next sibling, try parent's next sibling
657+
nextNode = comment.parentNode;
658+
while (nextNode && !nextNode.nextElementSibling && nextNode.parentNode) {
659+
nextNode = nextNode.parentNode;
660+
}
661+
if (nextNode && nextNode.nextElementSibling) {
662+
nextNode = nextNode.nextElementSibling;
663+
}
664+
}
665+
666+
if (!nextNode || nextNode.nodeType !== 1) return;
667+
668+
// Create the link element
669+
const link = document.createElement('a');
670+
link.href = url;
671+
link.textContent = shortName; // Use short name as link text
672+
link.title = url; // Show full URL on hover
673+
link.target = '_blank';
674+
link.style.cssText = `
675+
position: absolute;
676+
background: #ffeb3b;
677+
padding: 4px 8px;
678+
border: 1px solid #f57c00;
679+
border-radius: 3px;
680+
font-family: monospace;
681+
font-size: 12px;
682+
z-index: 10000;
683+
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
684+
color: #333;
685+
text-decoration: none;
686+
`;
687+
688+
// Position the link at the next element's position
689+
document.body.appendChild(link);
690+
691+
// Get position of the next element
692+
const rect = nextNode.getBoundingClientRect();
693+
link.style.left = rect.left + window.scrollX + 'px';
694+
link.style.top = rect.top + window.scrollY + 'px';
695+
link.className = 'iommi-code-finder-URL-link';
696+
});
697+
}
698+
699+
function remove_iommi_code_finder_links() {
700+
document.querySelectorAll('.iommi-code-finder-URL-link').forEach(link => {
701+
link.remove();
702+
});
703+
}

0 commit comments

Comments
 (0)