Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
46 changes: 30 additions & 16 deletions src-tauri/src/demo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ pub enum Filter {
FreeText(String),
Has(String),
MapName(String),
Not(Box<Filter>),
PlayerName(String),
TagName(String),
}
Expand All @@ -287,28 +288,28 @@ struct Filters {
free_text: Vec<String>,
has: Vec<String>,
map_name: Vec<String>,
not: Vec<Filters>,
player_name: Vec<String>,
tag_name: Vec<String>,
}

impl Filters {
fn from_filter_list(filters: &[Filter]) -> Self {
let mut result = Self::default();

for filter in filters {
match filter {
Filter::DemoType(value) => result.demo_type.push(value.to_lowercase()),
Filter::Event(value) => result.event.push(value.to_lowercase()),
Filter::FileName(value) => result.file_name.push(value.to_lowercase()),
Filter::FreeText(value) => result.free_text.push(value.to_lowercase()),
Filter::Has(value) => result.has.push(value.to_lowercase()),
Filter::MapName(value) => result.map_name.push(value.to_lowercase()),
Filter::PlayerName(value) => result.player_name.push(value.to_lowercase()),
Filter::TagName(value) => result.tag_name.push(value.to_lowercase()),
fn add_filter(&mut self, filter: &Filter) {
match filter {
Filter::DemoType(value) => self.demo_type.push(value.to_lowercase()),
Filter::Event(value) => self.event.push(value.to_lowercase()),
Filter::FileName(value) => self.file_name.push(value.to_lowercase()),
Filter::FreeText(value) => self.free_text.push(value.to_lowercase()),
Filter::Has(value) => self.has.push(value.to_lowercase()),
Filter::MapName(value) => self.map_name.push(value.to_lowercase()),
Filter::Not(filter) => {
let mut excluded = Self::default();
excluded.add_filter(filter);
self.not.push(excluded);
}
Filter::PlayerName(value) => self.player_name.push(value.to_lowercase()),
Filter::TagName(value) => self.tag_name.push(value.to_lowercase()),
}

result
}

fn matches(&self, demo: &Demo) -> bool {
Expand All @@ -320,6 +321,7 @@ impl Filters {
&& self.matches_map(demo)
&& self.matches_client(demo)
&& self.matches_tag(demo)
&& self.not.iter().all(|filter| !filter.matches(demo))
}

fn matches_type(&self, demo: &Demo) -> bool {
Expand Down Expand Up @@ -428,12 +430,24 @@ impl Filters {
}
}

impl<'a> FromIterator<&'a Filter> for Filters {
fn from_iter<T: IntoIterator<Item = &'a Filter>>(filters: T) -> Self {
let mut result = Self::default();

for filter in filters {
result.add_filter(filter);
}

result
}
}

pub fn sort_demos(demos: &mut [Arc<Demo>], sort_key: SortKey, reverse: bool) {
demos.sort_unstable_by(|d1, d2| compare_demos_by(sort_key, reverse, d1, d2));
}

pub fn filter_demos(demos: &[Arc<Demo>], filters: &[Filter]) -> Vec<Arc<Demo>> {
let filters = Filters::from_filter_list(filters);
let filters: Filters = filters.iter().collect();

demos
.iter()
Expand Down
1 change: 1 addition & 0 deletions src/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,5 +270,6 @@ export type DemoFilter =
| { free_text: string }
| { has: string }
| { map_name: string }
| { not: DemoFilter }
| { player_name: string }
| { tag_name: string };
4 changes: 2 additions & 2 deletions src/routes/demos/demoDir/KeyValueQueryLanguage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ export const keyValueQueryLanguage: QueryLanguage<Token, Parameters> = {
return tokens;
},
parser: (token: string, { filterPatterns }): Token => {
const matches = /^([a-zA-Z0-9-_]+):(.*)$/.exec(token);
const matches = /^(!?[a-zA-Z0-9-_]+):(.*)$/.exec(token);
if (matches !== null) {
const [_, key, value] = matches;

if (Object.keys(filterPatterns).includes(key)) {
if (Object.keys(filterPatterns).includes(key.replace(/^!/, ""))) {
return { type: "filter", value: { key, value } };
} else {
return { type: "invalid-filter", value: { key, value } };
Expand Down
8 changes: 8 additions & 0 deletions src/routes/demos/demoDir/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ function tokenToDemoFilter(token: Token): DemoFilter | null {
return null;
}

if (token.value.key.startsWith("!")) {
const filter = tokenToDemoFilter({
...token,
value: { ...token.value, key: token.value.key.slice(1) },
});
return filter === null ? null : { not: filter };
}

switch (token.value.key as FilterPatternKey | string) {
case "type":
return { demo_type: replaceBackslashPlaceholder(token.value.value) };
Expand Down
3 changes: 2 additions & 1 deletion src/routes/demos/demoDir/useAutocomplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
};
}, [queryText, cursorIndex]);

// biome-ignore lint/correctness/useExhaustiveDependencies(queryText): We want to detect changes in queryText

Check warning on line 48 in src/routes/demos/demoDir/useAutocomplete.tsx

View workflow job for this annotation

GitHub Actions / Check PR

suppressions/unused

Suppression comment has no effect. Remove the suppression or make sure you are suppressing the correct rule.
useEffect(() => {
if (
inputRef.current !== undefined &&
Expand All @@ -59,13 +59,14 @@
useEffect(() => {
if (currentTokenKeyValue === null) {
const keys = Object.keys(filterPatterns)
.map((key) => (currentToken.startsWith("!") ? `!${key}` : key))
.filter((key) => key.includes(currentToken))
.map((key) => `${key}:`);
setDropdownItems(keys);
} else {
const [currentTokenKey, currentTokenValue] = currentTokenKeyValue;

const values = filterPatterns[currentTokenKey];
const values = filterPatterns[currentTokenKey.replace(/^!/, "")];

if (values !== undefined) {
setDropdownItems(
Expand Down
Loading