mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-15 01:17:16 +00:00
Auto merge of #10834 - whee:gh-pages-retain-filter-state, r=xFrednet
Use URL parameters for filter states This fixes #8510 by storing Clippy Lints page filter configuration in the URL parameters. This includes: - Lint levels - Lint groups - Version filters "Filter" was already present in the URL and its behavior is retained. There is existing support for passing a `sel` query parameter; this is also retained, but I am not sure if it used in the wild. The URL parameters only get included if they are modified after loading the page. I have these changes available here in case people want to play with it: https://whee.github.io/rust-clippy/master/ An example with levels, groups, and versions set (oddly): https://whee.github.io/rust-clippy/master/#/?groups=pedantic,perf&levels=allow,warn&versions=gte:53,lte:57,eq:54 Adding a filter: https://whee.github.io/rust-clippy/master/#/manual_str_repeat?groups=pedantic,perf&levels=allow,warn&versions=gte:53,lte:57,eq:54 --- changelog: Docs: [`Clippy's lint list`] now stores filter parameters in the URL, to allow easy sharing [#10834](https://github.com/rust-lang/rust-clippy/pull/10834) <!-- changelog_checked -->
This commit is contained in:
commit
a97a94ab17
2 changed files with 183 additions and 30 deletions
|
@ -501,9 +501,11 @@ Otherwise, have a great day =^.^=
|
|||
<div class="col-12 col-md-7 search-control">
|
||||
<div class="input-group">
|
||||
<label class="input-group-addon" id="filter-label" for="search-input">Filter:</label>
|
||||
<input type="text" class="form-control filter-input" placeholder="Keywords or search string" id="search-input" ng-model="search" ng-model-options="{debounce: 50}"/>
|
||||
<input type="text" class="form-control filter-input" placeholder="Keywords or search string" id="search-input"
|
||||
ng-model="search" ng-blur="updatePath()" ng-keyup="$event.keyCode == 13 && updatePath()"
|
||||
ng-model-options="{debounce: 50}" />
|
||||
<span class="input-group-btn">
|
||||
<button class="filter-clear btn" type="button" ng-click="search = ''">
|
||||
<button class="filter-clear btn" type="button" ng-click="search = ''; updatePath();">
|
||||
Clear
|
||||
</button>
|
||||
</span>
|
||||
|
@ -517,7 +519,8 @@ Otherwise, have a great day =^.^=
|
|||
<h2 class="panel-title">
|
||||
<div class="panel-title-name">
|
||||
<span>{{lint.id}}</span>
|
||||
<a href="#{{lint.id}}" class="anchor label label-default" ng-click="open[lint.id] = true; $event.stopPropagation()">¶</a>
|
||||
<a href="#{{lint.id}}" class="anchor label label-default"
|
||||
ng-click="openLint(lint); $event.preventDefault(); $event.stopPropagation()">¶</a>
|
||||
<a href="" id="clipboard-{{lint.id}}" class="anchor label label-default" ng-click="copyToClipboard(lint); $event.stopPropagation()">
|
||||
📋
|
||||
</a>
|
||||
|
|
|
@ -24,9 +24,9 @@
|
|||
target.scrollIntoView();
|
||||
}
|
||||
|
||||
function scrollToLintByURL($scope) {
|
||||
var removeListener = $scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) {
|
||||
scrollToLint(window.location.hash.slice(1));
|
||||
function scrollToLintByURL($scope, $location) {
|
||||
var removeListener = $scope.$on('ngRepeatFinished', function (ngRepeatFinishedEvent) {
|
||||
scrollToLint($location.path().substring(1));
|
||||
removeListener();
|
||||
});
|
||||
}
|
||||
|
@ -106,10 +106,10 @@
|
|||
}
|
||||
};
|
||||
})
|
||||
.controller("lintList", function ($scope, $http, $timeout) {
|
||||
.controller("lintList", function ($scope, $http, $location, $timeout) {
|
||||
// Level filter
|
||||
var LEVEL_FILTERS_DEFAULT = {allow: true, warn: true, deny: true, none: true};
|
||||
$scope.levels = LEVEL_FILTERS_DEFAULT;
|
||||
$scope.levels = { ...LEVEL_FILTERS_DEFAULT };
|
||||
$scope.byLevels = function (lint) {
|
||||
return $scope.levels[lint.level];
|
||||
};
|
||||
|
@ -146,6 +146,165 @@
|
|||
"=": {enabled: false, minorVersion: null },
|
||||
};
|
||||
|
||||
// Map the versionFilters to the query parameters in a way that is easier to work with in a URL
|
||||
const versionFilterKeyMap = {
|
||||
"≥": "gte",
|
||||
"≤": "lte",
|
||||
"=": "eq"
|
||||
};
|
||||
const reverseVersionFilterKeyMap = Object.fromEntries(
|
||||
Object.entries(versionFilterKeyMap).map(([key, value]) => [value, key])
|
||||
);
|
||||
|
||||
// loadFromURLParameters retrieves filter settings from the URL parameters and assigns them
|
||||
// to corresponding $scope variables.
|
||||
function loadFromURLParameters() {
|
||||
// Extract parameters from URL
|
||||
const urlParameters = $location.search();
|
||||
|
||||
// Define a helper function that assigns URL parameters to a provided scope variable
|
||||
const handleParameter = (parameter, scopeVariable, defaultValues) => {
|
||||
if (urlParameters[parameter]) {
|
||||
const items = urlParameters[parameter].split(',');
|
||||
for (const key in scopeVariable) {
|
||||
if (scopeVariable.hasOwnProperty(key)) {
|
||||
scopeVariable[key] = items.includes(key);
|
||||
}
|
||||
}
|
||||
} else if (defaultValues) {
|
||||
for (const key in defaultValues) {
|
||||
if (scopeVariable.hasOwnProperty(key)) {
|
||||
scopeVariable[key] = defaultValues[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleParameter('levels', $scope.levels, LEVEL_FILTERS_DEFAULT);
|
||||
handleParameter('groups', $scope.groups, GROUPS_FILTER_DEFAULT);
|
||||
|
||||
// Handle 'versions' parameter separately because it needs additional processing
|
||||
if (urlParameters.versions) {
|
||||
const versionFilters = urlParameters.versions.split(',');
|
||||
for (const versionFilter of versionFilters) {
|
||||
const [key, minorVersion] = versionFilter.split(':');
|
||||
const parsedMinorVersion = parseInt(minorVersion);
|
||||
|
||||
// Map the key from the URL parameter to its original form
|
||||
const originalKey = reverseVersionFilterKeyMap[key];
|
||||
|
||||
if (originalKey in $scope.versionFilters && !isNaN(parsedMinorVersion)) {
|
||||
$scope.versionFilters[originalKey].enabled = true;
|
||||
$scope.versionFilters[originalKey].minorVersion = parsedMinorVersion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load the search parameter from the URL path
|
||||
const searchParameter = $location.path().substring(1); // Remove the leading slash
|
||||
if (searchParameter) {
|
||||
$scope.search = searchParameter;
|
||||
$scope.open[searchParameter] = true;
|
||||
scrollToLintByURL($scope, $location);
|
||||
}
|
||||
}
|
||||
|
||||
// updateURLParameter updates the URL parameter with the given key to the given value
|
||||
function updateURLParameter(filterObj, urlKey, defaultValue = {}, processFilter = filter => filter) {
|
||||
const parameter = Object.keys(filterObj)
|
||||
.filter(filter => filterObj[filter])
|
||||
.sort()
|
||||
.map(processFilter)
|
||||
.filter(Boolean) // Filters out any falsy values, including null
|
||||
.join(',');
|
||||
|
||||
const defaultParameter = Object.keys(defaultValue)
|
||||
.filter(filter => defaultValue[filter])
|
||||
.sort()
|
||||
.map(processFilter)
|
||||
.filter(Boolean) // Filters out any falsy values, including null
|
||||
.join(',');
|
||||
|
||||
// if we ended up back at the defaults, just remove it from the URL
|
||||
if (parameter === defaultParameter) {
|
||||
$location.search(urlKey, null);
|
||||
} else {
|
||||
$location.search(urlKey, parameter || null);
|
||||
}
|
||||
}
|
||||
|
||||
// updateVersionURLParameter updates the version URL parameter with the given version filters
|
||||
function updateVersionURLParameter(versionFilters) {
|
||||
updateURLParameter(
|
||||
versionFilters,
|
||||
'versions', {},
|
||||
versionFilter => versionFilters[versionFilter].enabled && versionFilters[versionFilter].minorVersion != null
|
||||
? `${versionFilterKeyMap[versionFilter]}:${versionFilters[versionFilter].minorVersion}`
|
||||
: null
|
||||
);
|
||||
}
|
||||
|
||||
// updateAllURLParameters updates all the URL parameters with the current filter settings
|
||||
function updateAllURLParameters() {
|
||||
updateURLParameter($scope.levels, 'levels', LEVEL_FILTERS_DEFAULT);
|
||||
updateURLParameter($scope.groups, 'groups', GROUPS_FILTER_DEFAULT);
|
||||
updateVersionURLParameter($scope.versionFilters);
|
||||
}
|
||||
|
||||
// Add $watches to automatically update URL parameters when the data changes
|
||||
$scope.$watch('levels', function (newVal, oldVal) {
|
||||
if (newVal !== oldVal) {
|
||||
updateURLParameter(newVal, 'levels', LEVEL_FILTERS_DEFAULT);
|
||||
}
|
||||
}, true);
|
||||
|
||||
$scope.$watch('groups', function (newVal, oldVal) {
|
||||
if (newVal !== oldVal) {
|
||||
updateURLParameter(newVal, 'groups', GROUPS_FILTER_DEFAULT);
|
||||
}
|
||||
}, true);
|
||||
|
||||
$scope.$watch('versionFilters', function (newVal, oldVal) {
|
||||
if (newVal !== oldVal) {
|
||||
updateVersionURLParameter(newVal);
|
||||
}
|
||||
}, true);
|
||||
|
||||
// Watch for changes in the URL path and update the search and lint display
|
||||
$scope.$watch(function () { return $location.path(); }, function (newPath) {
|
||||
const searchParameter = newPath.substring(1);
|
||||
if ($scope.search !== searchParameter) {
|
||||
$scope.search = searchParameter;
|
||||
$scope.open[searchParameter] = true;
|
||||
scrollToLintByURL($scope, $location);
|
||||
}
|
||||
});
|
||||
|
||||
let debounceTimeout;
|
||||
$scope.$watch('search', function (newVal, oldVal) {
|
||||
if (newVal !== oldVal) {
|
||||
if (debounceTimeout) {
|
||||
$timeout.cancel(debounceTimeout);
|
||||
}
|
||||
|
||||
debounceTimeout = $timeout(function () {
|
||||
$location.path(newVal);
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$watch(function () { return $location.search(); }, function (newParameters) {
|
||||
loadFromURLParameters();
|
||||
}, true);
|
||||
|
||||
$scope.updatePath = function () {
|
||||
if (debounceTimeout) {
|
||||
$timeout.cancel(debounceTimeout);
|
||||
}
|
||||
|
||||
$location.path($scope.search);
|
||||
}
|
||||
|
||||
$scope.selectTheme = function (theme) {
|
||||
setTheme(theme, true);
|
||||
}
|
||||
|
@ -169,10 +328,9 @@
|
|||
};
|
||||
|
||||
$scope.resetGroupsToDefault = function () {
|
||||
const groups = $scope.groups;
|
||||
for (const [key, value] of Object.entries(GROUPS_FILTER_DEFAULT)) {
|
||||
groups[key] = value;
|
||||
}
|
||||
$scope.groups = {
|
||||
...GROUPS_FILTER_DEFAULT
|
||||
};
|
||||
};
|
||||
|
||||
$scope.selectedValuesCount = function (obj) {
|
||||
|
@ -272,6 +430,12 @@
|
|||
return true;
|
||||
}
|
||||
|
||||
// Show details for one lint
|
||||
$scope.openLint = function (lint) {
|
||||
$scope.open[lint.id] = true;
|
||||
$location.path(lint.id);
|
||||
};
|
||||
|
||||
$scope.copyToClipboard = function (lint) {
|
||||
const clipboard = document.getElementById("clipboard-" + lint.id);
|
||||
if (clipboard) {
|
||||
|
@ -296,14 +460,12 @@
|
|||
// Get data
|
||||
$scope.open = {};
|
||||
$scope.loading = true;
|
||||
|
||||
// This will be used to jump into the source code of the version that this documentation is for.
|
||||
$scope.docVersion = window.location.pathname.split('/')[2] || "master";
|
||||
|
||||
if (window.location.hash.length > 1) {
|
||||
$scope.search = window.location.hash.slice(1);
|
||||
$scope.open[window.location.hash.slice(1)] = true;
|
||||
scrollToLintByURL($scope);
|
||||
}
|
||||
// Set up the filters from the URL parameters before we start loading the data
|
||||
loadFromURLParameters();
|
||||
|
||||
$http.get('./lints.json')
|
||||
.success(function (data) {
|
||||
|
@ -315,7 +477,7 @@
|
|||
selectGroup($scope, selectedGroup.toLowerCase());
|
||||
}
|
||||
|
||||
scrollToLintByURL($scope);
|
||||
scrollToLintByURL($scope, $location);
|
||||
|
||||
setTimeout(function () {
|
||||
var el = document.getElementById('filter-input');
|
||||
|
@ -326,18 +488,6 @@
|
|||
$scope.error = data;
|
||||
$scope.loading = false;
|
||||
});
|
||||
|
||||
window.addEventListener('hashchange', function () {
|
||||
// trigger re-render
|
||||
$timeout(function () {
|
||||
$scope.levels = LEVEL_FILTERS_DEFAULT;
|
||||
$scope.search = window.location.hash.slice(1);
|
||||
$scope.open[window.location.hash.slice(1)] = true;
|
||||
|
||||
scrollToLintByURL($scope);
|
||||
});
|
||||
return true;
|
||||
}, false);
|
||||
});
|
||||
})();
|
||||
|
||||
|
|
Loading…
Reference in a new issue