Skip to content
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,18 @@ How to define and use tags is described in the [Tags](docs/Tags.md) documentatio
## Search

It is possible to filter or search for repositories using the search box at the top of RepoM.
RepoM also supports quick filters, which let you save searches, re-use tag filters, and toggle built-in filters such as favorites and active repositories.
See the [Search](docs/search.md) for more information.

## Ordering and Filtering

The order of the repositories shown in RepoM is customizable. The default is alphabetical on the repository name. Read more about ordering [here](docs/_old/Ordering.md).
The order of the repositories shown in RepoM is customizable. The default is alphabetical on the repository name. Read more in the [Ordering documentation](docs/_old/Ordering.md).

The repositories shown in RepoM are filtered using the search box in RepoM. But the default set can also be configured using different presets.

## Global configuration

When RepoM starts for the first time, a configuration file wil be created. Most of the properties can be adjusted using the UI but, at this moment, one property must be altered manually. Read more over [here](docs/_old/Settings.md).
When RepoM starts for the first time, a configuration file wil be created. Most of the properties can be adjusted using the UI but, at this moment, one property must be altered manually. Read more in the [Settings documentation](docs/_old/Settings.md).

## Multi configuration

Expand Down
5 changes: 3 additions & 2 deletions README.source.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,18 @@ How to define and use tags is described in the [Tags](docs/Tags.md) documentatio
## Search

It is possible to filter or search for repositories using the search box at the top of RepoM.
RepoM also supports quick filters, which let you save searches, re-use tag filters, and toggle built-in filters such as favorites and active repositories.
See the [Search](docs/search.md) for more information.

## Ordering and Filtering

The order of the repositories shown in RepoM is customizable. The default is alphabetical on the repository name. Read more about ordering [here](docs/_old/Ordering.md).
The order of the repositories shown in RepoM is customizable. The default is alphabetical on the repository name. Read more in the [Ordering documentation](docs/_old/Ordering.md).

The repositories shown in RepoM are filtered using the search box in RepoM. But the default set can also be configured using different presets.

## Global configuration

When RepoM starts for the first time, a configuration file wil be created. Most of the properties can be adjusted using the UI but, at this moment, one property must be altered manually. Read more over [here](docs/_old/Settings.md).
When RepoM starts for the first time, a configuration file wil be created. Most of the properties can be adjusted using the UI but, at this moment, one property must be altered manually. Read more in the [Settings documentation](docs/_old/Settings.md).

## Multi configuration

Expand Down
13 changes: 9 additions & 4 deletions docs/Tags.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Tags can be set to repositories and are used for searching, filtering, and order
Tags can be defined in the `TagsV2.yaml` file located in `%APPDATA%\RepoM\` folder.
The yaml consists of two parts. First an optional context section, and second the tags section.

The context is the same context you use defining the action menu's. It allows you to add or define methods and variables. More information over the context can be found [here](Context.md)
The context is the same context you use defining the action menus. It allows you to add or define methods and variables. See [Context](Context.md) for more information.

The 'tags' section of the yaml is an array of tag specifications. The property `tag` is a string and represents the name of the tag. The optional property `when` is a predicate condition when to apply the tag. The predicate is evaluated using Scriban.

Expand Down Expand Up @@ -68,7 +68,9 @@ tags:

First, after applying tags, you noticat that they become visible in the UI. Although this is nice, it doesn't do anything for you at the moment.

The power of defining tags is currently the possiblility to search by a tag. For this to work, you need to have selected the 'Lucene query parser'.
The power of defining tags is currently the possibility to search by a tag. For this to work, you need to have selected the 'Lucene query parser'.

Tags also integrate with quick filters in the UI. Clicking a tag on a repository row creates or activates a quick filter for that tag, so you can keep using that tag-based filter without retyping it.

### Search

Expand All @@ -84,8 +86,11 @@ For now, all regular text is also matched against tags.

### Filter

TODO
See filtering.
Tags can also be used through quick filters.

- Clicking a visible tag in the repository list creates or activates a quick filter for that tag.
- That quick filter can then be toggled on, off, or inverse like any other quick filter.
- Multiple tag-based quick filters can be combined with other quick filters and with the search box.

### Ordering

Expand Down
122 changes: 102 additions & 20 deletions docs/search.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,128 @@
# Search

After opening RepoM, you can search for respositories using the searchbox at the top.
After opening RepoM, you can narrow down the repository list in two ways:

By default, this box enabled you to search for repositories by simply matching repository names (i.e., the default query parser is very limited).
- by typing in the search box at the top,
- by enabling one or more quick filters below the search box.

It is possible to use a different query parser enabling the use of complex queries.
The search box is good for ad-hoc filtering. Quick filters are useful when you want to keep a search around and re-use it with a single click.

## Search box

By default, the search box uses the currently selected query parser.

The default parser is intentionally simple and is mainly suited for free-text matching on repository names and tags. If you need field-based queries such as `tag:work` or `is:favorite`, select a more capable parser such as the Lucene query parser.

The current search text is always combined with the active quick filters. In practice this means that the visible repository list must match both the search box and the enabled quick filters.

## Quick filters

Quick filters are persistent filter chips shown directly below the search box. A quick filter stores a parsed query and lets you enable, disable, invert, rename, and re-order that query without having to type it again.

### What quick filters can do

- Save a search query and re-use it later.
- Turn a repository tag into a reusable filter.
- Quickly switch common filters on and off.
- Invert a filter so matching repositories are excluded instead of included.
- Combine multiple quick filters to build a narrower result set.
- Keep custom labels and tooltips for filters you use often.

### How to create quick filters

There are three ways to get a quick filter:

1. Type a query in the search box and click the pin button.
2. Click a tag on a repository item to create or activate a `tag:<name>` filter.
3. Use the built-in quick filters that are always available.

When you save a search, RepoM parses the text with the currently selected query parser and stores the resulting query. Saving the same query again does not create duplicates; RepoM activates the existing quick filter instead.

### Built-in quick filters

RepoM currently includes two built-in quick filters:

- `Favorites`: matches repositories that are marked as favorite. Internally this is the same as `is:favorite`.
- `Active`: matches repositories with monitoring enabled. Internally this is the same as `is:active`.

Built-in quick filters are always present. They cannot be renamed, deleted, or re-ordered.

### Toggle behavior

Clicking a quick filter cycles through three states:

1. Off: the quick filter is ignored.
2. On: only repositories matching the filter are included.
3. Inverse: repositories matching the filter are excluded.

This makes quick filters useful for both inclusion and exclusion scenarios. For example, a quick filter based on `tag:archived` can be inverted to hide archived repositories.

### Combining behavior

All active quick filters are combined using `AND`.

Examples:

- `Favorites` + `tag:work` shows only favorite work repositories.
- `Active` + `branch:main` shows only monitored repositories on the `main` branch.
- Inverted `tag:archived` + `tag:work` shows work repositories except archived ones.

The search box is also combined with the quick filters using `AND`. So if the search box contains `github` and you enable a quick filter for `tag:work`, RepoM only shows repositories that match both.

### Managing custom quick filters

Custom quick filters support a small context popup that lets you:

- change the label shown on the quick filter chip,
- add or change a tooltip,
- delete the quick filter.

Custom quick filters can also be re-ordered by dragging them. This is useful when you have a small set of frequently used filters and want them in a predictable order.

Custom quick filters are stored in `%APPDATA%\RepoM\quickfilters.json`.

### Practical examples

- Save `tag:work` as a quick filter if you often switch to work repositories.
- Save `is:favorite` if you prefer using a textual query instead of the built-in `Favorites` filter.
- Save `tag:github AND branch:main` when you often work on GitHub repositories on the main branch.
- Click the `private` tag on a repository row to create or activate a quick filter for that tag.

## Query Parser

The query parser is responsible for parsing a query string into a tree representation of this query. The leaves are so called query terms, and which can be combined with boolean operators.
The query parser is responsible for parsing a query string into a query tree. The leaves are query terms, which can be combined with boolean operators.

The default query parser takes the query string and creates a single 'FreeText' which will be evaluted using the corresponding matcher.
The active query parser is used in two places:

A suffisticated query parser can create for instance multiple terms combined using an 'OR' operator, so only one term has to match.
- while typing in the search box,
- when saving the current search as a quick filter.

Currently, the following query parsers are available:
The default query parser takes the query string and creates a single `FreeText` query which is evaluated using the corresponding matcher.

- Default (legacy, all text will be matched using FreeTextTerm matcher)
- LuceneQueryParser (provided using `LuceneQueryParser` plugin, which enables a suffisticated query syntax based on Lucene)
A more sophisticated query parser can create multiple terms combined using operators such as `AND`, `OR`, and `NOT`.

### Terms and Operators
Currently, the following query parsers are available:

sdf
- Default (legacy, all text will be matched using FreeTextTerm matcher)
- LuceneQueryParser (provided using `LuceneQueryParser` plugin, which enables a sophisticated query syntax based on Lucene)

### Query matchers

Terms are matched using query matchers. A list of query matchers can be found over [here](querymatchers.md).
Terms are matched using query matchers. See [Query matchers](querymatchers.md) for the available matchers.

## Terms

There are multiple terms which express intent. It is up to the different handlers how these terms are handled.

### FreeText

Free text without a term. The free text matcher currently matches the free text against the repository name or it's tags.

### SimpleTerm

sdf
Free text without a term. The free text matcher currently matches the free text against the repository name or its tags.

### StartsWithTerm

The start with term has a term and a value.

For example, the term `StartsWithTerm(term: 'branch', value: 'ma')` will be evaluated by a specific IQueryMatcher which checks if the
For example, the term `StartsWithTerm(term: 'branch', value: 'ma')` can be evaluated by a matcher that checks whether the branch starts with `ma`.

<!-- ### RangeTerm (Rename TermRange)

Expand Down Expand Up @@ -71,5 +150,8 @@ The `Not` operator accepts one term. The 'not query matcher' will inverse the ma

## Cheatsheet

- Use <kbd>Ctrl</kbd>+<kbd>F</kbd> to focus the search box
- Use <kbd>Esc</kbd> to clear the box, when already cleared, this key will close RepoM
- Use `Ctrl+F` to focus the search box
- Use `Esc` to clear the box; when already cleared, this key will close RepoM
- Use the pin button to save the current search as a quick filter
- Click a quick filter to cycle through off, on, and inverse
- Click a repository tag to create or activate a tag-based quick filter
7 changes: 7 additions & 0 deletions src/RepoM.Api/Bootstrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace RepoM.Api;
using System.Threading.Tasks;
using System;
using RepoM.Api.Plugins.SimpleInjector;
using RepoM.Api.QuickFilter;
using RepoM.Core.Plugin;
using RepoM.Core.Plugin.Common;
using RepoM.Core.Plugin.RepositoryOrdering.Configuration;
Expand Down Expand Up @@ -48,6 +49,12 @@ public static void RegisterRepositoryComparerConfigurationsTypes(Container conta
}
}

public static void RegisterQuickFilterServices(Container container)
{
ArgumentNullException.ThrowIfNull(container);
container.Register<IQuickFilterService, QuickFilterService>(Lifestyle.Singleton);
}

public async Task LoadAndRegisterPluginsAsync(Container container, string baseDirectory)
{
_ = container ?? throw new ArgumentNullException(nameof(container));
Expand Down
36 changes: 36 additions & 0 deletions src/RepoM.Api/QuickFilter/IQuickFilterService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
namespace RepoM.Api.QuickFilter;

using System;
using System.Collections.Generic;
using RepoM.Core.Plugin.RepositoryFiltering.Clause;

public enum QuickFilterCombineMode
{
And,
Or,
}

public interface IQuickFilterService
{
QuickFilterCombineMode CombineMode { get; set; }

IReadOnlyList<QuickFilterModel> GetAll();

QuickFilterModel Add(string label, IQuery query);

void Remove(Guid id);

void SetActive(Guid id, bool isActive);

void SetInverse(Guid id, bool isInverse);

void UpdateLabel(Guid id, string newLabel);

void UpdateToolTip(Guid id, string newToolTip);

void UpdateOrder(Guid id, int newOrder);

QuickFilterModel? FindByQuery(IQuery query);

event EventHandler? Changed;
}
Loading
Loading