Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
24b4eda
[Local profiler] Filter sections by name
antoinem May 29, 2026
1bb1701
Show line numbers when greater than zero only
alexisbernard Jun 15, 2026
42f9f0c
Remove duplicated function and rename it to filterSections
alexisbernard Jun 15, 2026
8d7489a
Deduplicate restoreOriginalContent
alexisbernard Jun 15, 2026
aa2319e
Simplify branching
alexisbernard Jun 18, 2026
adb0908
Reuse Mustache.escape instead of having a custom function
alexisbernard Jun 18, 2026
09ec7d6
Restore initial state for low impact sections
alexisbernard Jun 18, 2026
7a85298
Get rid of attribute data-rorvswild-local-section-name
alexisbernard Jun 18, 2026
17b7e23
Lower query once
alexisbernard Jun 18, 2026
d353ebc
Renamings
alexisbernard Jun 18, 2026
c13e232
Replace DOM by Mustache template
alexisbernard Jun 18, 2026
f7c15e7
Fix currentExecution
alexisbernard Jun 19, 2026
4a22b07
Fix case insensitive highlight
alexisbernard Jun 19, 2026
8662dbb
Keep section open state when filtering
alexisbernard Jun 19, 2026
b6c4030
Fix search highlight
alexisbernard Jun 19, 2026
9f68c6f
Rename toggleCommand to toggleSection
alexisbernard Jun 19, 2026
5e184bb
Remove duplication
alexisbernard Jun 19, 2026
e9f3d9d
Fix potential XSS
alexisbernard Jun 19, 2026
82259ca
Move section filtering logic to request
alexisbernard Jun 19, 2026
c759da3
Remove dead CSS and cleanup
alexisbernard Jun 19, 2026
eb11693
Move filtering logic to Request
alexisbernard Jun 19, 2026
6b4947a
Rename RorVsWild.Local.Request to RorVsWild.Local.Execution
alexisbernard Jun 19, 2026
abb36a5
Split sections template to prevent from re-rendering search input
alexisbernard Jun 19, 2026
7e43c13
Replace data attributes by IDs for query selector
alexisbernard Jun 19, 2026
3393c1d
Simplifications
alexisbernard Jun 19, 2026
a790a41
Remove duplication
alexisbernard Jun 19, 2026
65c55d0
Optimize by computing coloration less often
alexisbernard Jun 19, 2026
c013434
Fix section highlighting
alexisbernard Jun 19, 2026
a201817
Remove unsued code
alexisbernard Jun 19, 2026
27216fa
Improve changelog
alexisbernard Jun 19, 2026
d131613
Remove duplicated template between job and request
alexisbernard Jun 19, 2026
30825d5
Fix missing closing tags
alexisbernard Jun 19, 2026
0165623
Remove code
alexisbernard Jun 19, 2026
753d69e
Don't duplicate sections
alexisbernard Jun 19, 2026
eb3ac35
Use this instead of self
alexisbernard Jun 19, 2026
895f817
Fix typo
alexisbernard Jun 19, 2026
7ae65af
Merge branch 'master' into filter_text
alexisbernard Jun 19, 2026
b496635
Merge branch 'master' into filter_text
alexisbernard Jun 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
## Unreleased

* Local profiler:
* Filter by section kind in sections breakdown
* Filter sections by kind in sections breakdown
* Filter sections by file name

## 1.11.1 (2026-03-16)

Expand Down
199 changes: 127 additions & 72 deletions lib/rorvswild/local/javascript/local.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ RorVsWild.Local = function(container) {

RorVsWild.Local.prototype.getRequests = function(callback) {
this.getJson("/rorvswild/requests.json", function(data) {
this.requests = data.map(function(attributes) { return new RorVsWild.Local.Request(attributes) })
this.requests = data.map(function(attributes) { return new RorVsWild.Local.Execution(attributes) })
callback()
}.bind(this))
}

RorVsWild.Local.prototype.getJobs = function(callback) {
this.getJson("/rorvswild/jobs.json", function(data) {
this.jobs = data.map(function(attributes) { return new RorVsWild.Local.Request(attributes) })
this.jobs = data.map(function(attributes) { return new RorVsWild.Local.Execution(attributes) })
callback()
}.bind(this))
}
Expand Down Expand Up @@ -54,7 +54,6 @@ RorVsWild.Local.prototype.getJson = function(path, callback) {
RorVsWild.Local.prototype.render = function(view) {
this.view = view
Barber.render("RorVsWild.Local", this, this.root)
Prism.highlightAllUnder(this.root)
}

RorVsWild.Local.prototype.renderBody = function() {
Expand All @@ -70,59 +69,20 @@ RorVsWild.Local.prototype.toggle = function(event) {
this.active ? this.collapse() : this.expand(event)
}

RorVsWild.Local.prototype.toggleCommand = function(event) {
document.querySelector(event.currentTarget.dataset.target).classList.toggle("is-open")
}

RorVsWild.Local.prototype.resetLowImpactSections = function() {
var button = document.querySelector("[data-rorvswild-local-toggle-low-impact]")
document.querySelectorAll("[data-rorvswild-local-low-impact]").forEach(function(section) {
section.classList.add("is-hidden")
})
if (button) button.textContent = "↓ View low impact sections"
}

RorVsWild.Local.prototype.toggleLowImpactSections = function(event) {
var sections = document.querySelectorAll("[data-rorvswild-local-low-impact]")
var isHidden = sections[0].classList.contains("is-hidden")

if (isHidden) {
sections.forEach(function(section) { section.classList.remove("is-hidden") })
event.currentTarget.textContent = "↑ Hide low impact sections"
} else {
this.resetLowImpactSections()
}
}

RorVsWild.Local.prototype.filterByKind = function(event) {
var li = event.currentTarget
var kind = li.dataset.kind
var isActive = li.classList.contains("is-active")
var toggleButton = document.querySelector("[data-rorvswild-local-toggle-low-impact]")

document.querySelectorAll("[data-rorvswild-local-breakdown-item]").forEach(function(el) {
el.classList.remove("is-active")
})

var sections = document.querySelectorAll("[data-rorvswild-local-section]")

if (isActive) {
this.resetLowImpactSections()
sections.forEach(function(section) {
if (!section.hasAttribute("data-rorvswild-local-low-impact")) section.classList.remove("is-hidden")
})
if (toggleButton) toggleButton.style.display = ""
} else {
li.classList.add("is-active")
sections.forEach(function(section) {
if (section.dataset.kind === kind) {
section.classList.remove("is-hidden")
} else {
section.classList.add("is-hidden")
}
})
if (toggleButton) toggleButton.style.display = "none"
RorVsWild.Local.highlightMatches = function(text, query) {
if (query.length < 1)
return Mustache.escape(text)
var lower = text.toLowerCase()
var result = "", start = 0, index
while ((index = lower.indexOf(query, start)) !== -1) {
result += Mustache.escape(text.slice(start, index))
result += '<mark class="rorvswild-local-panel__section-filter-highlight">'
result += Mustache.escape(text.slice(index, index + query.length))
result += "</mark>"
start = index + query.length
}
result += Mustache.escape(text.slice(start))
return result
}

RorVsWild.Local.prototype.expand = function() {
Expand Down Expand Up @@ -165,11 +125,17 @@ RorVsWild.Local.formatDateTime = function(date) {
return [date.getDate(), months[date.getMonth()], hours + ":" + minutes].join(" ")
}

RorVsWild.Local.prototype.goToExecutionDetail = function(execution) {
this.currentExecution = execution
execution.resetSectionFilters()
execution.loadSections()
this.render("Execution")
execution.renderSections()
}

RorVsWild.Local.prototype.goToRequestDetail = function(event) {
this.root.dataset.tab = "requests"
var uuid = event.currentTarget.dataset.uuid
this.currentRequest = this.requests.find(function(req) { return req.uuid == uuid })
this.render("RequestDetail")
this.goToExecutionDetail(this.requests.find(function(req) { return req.uuid == uuid }))
}

RorVsWild.Local.prototype.goToRequestIndex = function(event) {
Expand All @@ -184,8 +150,7 @@ RorVsWild.Local.prototype.goToJobIndex = function(event) {

RorVsWild.Local.prototype.goToJobDetail = function(event) {
var uuid = event.currentTarget.dataset.uuid
this.currentJob = this.jobs.find(function(job) { return job.uuid == uuid })
this.render("JobDetail")
this.goToExecutionDetail(this.jobs.find(function(job) { return job.uuid == uuid }))
}

RorVsWild.Local.prototype.goToErrors = function(event) {
Expand All @@ -197,6 +162,7 @@ RorVsWild.Local.prototype.goToErrorDetail = function(event) {
var uuid = event.currentTarget.dataset.uuid
this.currentError = this.errors.find(function(err) { return err.uuid == uuid })
this.render("ErrorDetail")
Prism.highlightAllUnder(this.root)
}

RorVsWild.Local.prototype.containerClass = function() {
Expand Down Expand Up @@ -226,24 +192,48 @@ RorVsWild.Local.nextId = function() {
return RorVsWild.Local.lastId += 1
}

RorVsWild.Local.Request = function(data) {
RorVsWild.Local.Execution = function(data) {
this.data = data
this.uuid = data.uuid
this.name = data.name
this.path = data.path
this.queuedAt = RorVsWild.Local.formatDateTime(new Date(data.queued_at))
this.sections = []
}

RorVsWild.Local.Request.prototype.runtime = function() {
RorVsWild.Local.Execution.prototype.runtime = function() {
return RorVsWild.Local.relevantRounding(this.data.runtime)
}

RorVsWild.Local.Request.prototype.sections = function() {
return this.data.sections.map(function(section) {
RorVsWild.Local.Execution.prototype.resetSectionFilters = function() {
this.sectionKindFilter = null
this.sectionQueryFilter = ""
this.lowImpactExpanded = false
}

RorVsWild.Local.Execution.prototype.isFilteringSections = function() {
return this.sectionKindFilter != null || this.sectionQueryFilter.length > 0
}

RorVsWild.Local.Execution.prototype.filterSections = function() {
var kind = this.sectionKindFilter
var query = this.sectionQueryFilter
var filtering = this.isFilteringSections()

return this.sections.filter(function(section) {
return (!kind || section.kind === kind)
&& (query.length < 1 || section.location.toLowerCase().indexOf(query) !== -1)
&& (filtering || this.lowImpactExpanded || !section.isLowImpact)
}.bind(this))
}

RorVsWild.Local.Execution.prototype.loadSections = function() {
this.sections = this.data.sections.map(function(section) {
var runtime = (section.total_runtime - section.children_runtime)
var impactValue = runtime * 100 / this.data.runtime
return {
id: RorVsWild.Local.nextId(),
isOpen: false,
impact: RorVsWild.Local.formatImpact(impactValue),
isLowImpact: impactValue < 1,
language: RorVsWild.Local.kindToLanguage(section.kind),
Expand All @@ -258,27 +248,92 @@ RorVsWild.Local.Request.prototype.sections = function() {
kind: section.kind.substring(0, 7),
file: section.file,
line: section.line,
isLineRelevant: section.line > 0,
location: section.kind == "view" ? section.file : section.file + ":" + section.line,
location: section.file + (section.line > 0 ? ":" + section.line : ""),
locationUrl: RorVsWild.Local.pathToUrl(this.data.environment.cwd, section.file, section.line),
isAsync: RorVsWild.Local.relevantRounding(section.async_runtime) > 0,
}
}.bind(this)).sort(function(a, b) { return b.selfRuntime - a.selfRuntime })
}

RorVsWild.Local.Request.prototype.hasLowImpactSections = function() {
return this.sections().some(function(section) { return section.isLowImpact })
}
RorVsWild.Local.Execution.prototype.sectionsImpactPerKind = function() {
if (!this.sections.length) return []

RorVsWild.Local.Request.prototype.sectionsImpactPerKind = function() {
var total = 0
var perKind = this.sections().reduce(function(object, section) {
var perKind = this.sections.reduce(function(object, section) {
object[section.kind] = (object[section.kind] || 0) + section.runtime
total += section.runtime
return object
}, {})
return Object.entries(perKind).sort(function(a, b) { return b[1] - a[1] })
.map(function(item) { return {kind: item[0], impact: Math.round((item[1] / total * 100) * 10) / 10} })
.map(function(item) {
return {
kind: item[0],
impact: Math.round((item[1] / total * 100) * 10) / 10,
isActive: item[0] === this.sectionKindFilter
}
}.bind(this))
}

RorVsWild.Local.Execution.prototype.showLowImpactToggle = function() {
return !this.isFilteringSections() && this.sections.some(function(section) { return section.isLowImpact })
}

RorVsWild.Local.Execution.prototype.lowImpactToggleLabel = function() {
return this.lowImpactExpanded ? "↑ Hide low impact sections" : "↓ View low impact sections"
}

RorVsWild.Local.Execution.prototype.renderSectionHeader = function() {
var container = document.querySelector("#rorvswild-local-sections-header")
if (!container) return

Barber.render("RorVsWild.Local.Sections.Header", this, container)
}

RorVsWild.Local.Execution.prototype.renderSectionList = function() {
(this.filteredSections = this.filterSections()).forEach(function(section) {
section.locationHtml = RorVsWild.Local.highlightMatches(section.location, this.sectionQueryFilter)
}.bind(this))

var container = document.querySelector("#rorvswild-local-sections-list")
Barber.render("RorVsWild.Local.Sections.List", this, container)

this.filteredSections.forEach(function(section) {
section.isOpen && Prism.highlightAllUnder(container.querySelector("#section-" + section.id))
})
}

RorVsWild.Local.Execution.prototype.renderSections = function() {
this.renderSectionHeader()
this.renderSectionList()
}

RorVsWild.Local.Execution.prototype.toggleSection = function(event) {
var id = parseInt(event.currentTarget.dataset.sectionId, 10)
var section = this.sections.find(function(section) { return section.id === id })
if (!section) return

section.isOpen = !section.isOpen
var element = event.currentTarget.closest(".rorvswild-local-panel__details__section")
element.classList.toggle("is-open", section.isOpen)
if (section.isOpen)
Prism.highlightAllUnder(element)
}

RorVsWild.Local.Execution.prototype.toggleLowImpactSections = function() {
this.lowImpactExpanded = !this.lowImpactExpanded
this.renderSectionList()
}

RorVsWild.Local.Execution.prototype.filterSectionsByText = function(event) {
this.sectionQueryFilter = event.currentTarget.value.trim().toLowerCase()
this.renderSectionList()
}

RorVsWild.Local.Execution.prototype.filterSectionsByKind = function(event) {
var kind = event.currentTarget.dataset.kind
this.sectionKindFilter = this.sectionKindFilter === kind ? null : kind
this.renderSectionHeader()
this.renderSectionList()
}

RorVsWild.Local.Error = function(data) {
Expand Down
Loading
Loading