Skip to content
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9a46457
Search UX part 1: Result selection and shortcuts.
tristanlatr Nov 9, 2025
d80d015
Search UX part 2: If the search box contains "docstring:<term>" enabl…
tristanlatr Nov 9, 2025
7001716
Search UX part 3: Use a logical "and" in between search terms by defa…
tristanlatr Nov 9, 2025
6d3b3c8
Search UX part 4: Include the field "kind" in the default index and a…
tristanlatr Nov 9, 2025
e0a36c7
Don't use document's boosts. It's rather confusing to have a search r…
tristanlatr Nov 10, 2025
8624eae
Fix annotation
tristanlatr Nov 10, 2025
3388a4b
Order is not important for these search results
tristanlatr Nov 11, 2025
5b428c6
Apply suggestions from code review
tristanlatr Nov 13, 2025
b245ee2
Merge branch 'master' into 822-search-improvements
tristanlatr Nov 30, 2025
7d85afd
Merge branch 'master' into 822-search-improvements
tristanlatr Nov 30, 2025
a7cee8f
Merge branch 'master' into 822-search-improvements
tristanlatr Nov 30, 2025
bce249d
Merge branch 'master' into 822-search-improvements
tristanlatr Nov 30, 2025
07a4254
Use a better box shadow for the selected results
tristanlatr Dec 11, 2025
cf4bb4b
Says when the JS files are still loading
tristanlatr Dec 11, 2025
8cf66b9
Reload queyr when pressing enter and no result is selected
tristanlatr Dec 11, 2025
dc47343
Merge branch 'master' into 822-search-improvements
tristanlatr Dec 11, 2025
e91ff5e
Ue background color property instead of box-shadow for the selected i…
tristanlatr Dec 12, 2025
02ec283
Merge remote-tracking branch 'origin/822-search-improvements' into 82…
tristanlatr Dec 12, 2025
2167074
Merge branch 'master' into 822-search-improvements
tristanlatr Jan 24, 2026
d159787
Merge branch 'master' into 822-search-improvements
tristanlatr Jan 28, 2026
e9db5b5
Apply suggestions from code review
tristanlatr Jan 28, 2026
d1576fb
Add important statement to CSS such that selection style also applies…
tristanlatr Jan 29, 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
9 changes: 9 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,21 @@ What's New?
in development
^^^^^^^^^^^^^^

* Improve the search box UX:
- There is now a keyboard shortcut ('Ctrl+K' or 'Cmd+K' on Mac or '/',
but the later is overriden by ReadTheDocs) to focus the search box.
When the search box is focused, you can use the up and down arrow keys to navigate the results,
and press enter to open the selected result.
- If the search box contains "docstring:<term>", the search will be performed in docstrings automatically.
- Use a logical "and" in between search terms by default. Use leading '?' to make a term optional.
(More on this in the embedded help page of the generated documentation.)
* Hide sidebar element title when all items under it are private.
* Allow suppressing the footer's buildtime altogether with option ``--buildtime=no``.
* Add project version on each HTML page.
* When an intersphinx inventory file fails to read, exit with code 2 and do not print the whole stack trace by default.
The -v flag will log exceptions' tracebacks.


pydoctor 25.10.1
^^^^^^^^^^^^^^^^

Expand Down
2 changes: 1 addition & 1 deletion docs/tests/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ def test_search(query:str, expected:List[str], order_is_important:bool=True) ->
'pydoctor.factory.Factory.Class',
'pydoctor.model.DocumentableKind.CLASS',
'pydoctor.model.System.Class',
])
], order_is_important=False)

to_stan_results = [
'pydoctor.epydoc.markup.ParsedDocstring.to_stan',
Expand Down
29 changes: 8 additions & 21 deletions pydoctor/templatewriter/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from __future__ import annotations

from pathlib import Path
from typing import Iterator, List, Optional, Tuple, Type, Dict, TYPE_CHECKING
from typing import Iterator, List, Optional, Type, Dict, TYPE_CHECKING
import json

import attr
Expand Down Expand Up @@ -63,7 +63,7 @@ class LunrIndexWriter:
fields: List[str]

_BOOSTS = {
'name':6,
'name':3,
'names': 1,
'qname':2,
'docstring':1,
Expand All @@ -75,14 +75,6 @@ class LunrIndexWriter:
_SKIP_PIPELINES = list(_BOOSTS)
_SKIP_PIPELINES.remove('docstring')

@staticmethod
def get_ob_boost(ob: model.Documentable) -> int:
# Advantage container types because they hold more informations.
if isinstance(ob, (model.Class, model.Module)):
return 2
else:
return 1

def format(self, ob: model.Documentable, field:str) -> Optional[str]:
try:
return getattr(self, f'format_{field}')(ob) #type:ignore[no-any-return]
Expand Down Expand Up @@ -110,16 +102,11 @@ def format_docstring(self, ob: model.Documentable) -> Optional[str]:
def format_kind(self, ob:model.Documentable) -> str:
return epydoc2stan.format_kind(ob.kind) if ob.kind else ''

def get_corpus(self) -> List[Tuple[Dict[str, Optional[str]], Dict[str, int]]]:
def get_corpus(self) -> list[dict[str, str | None]]:
return [
(
{
f:self.format(ob, f) for f in self.fields
},
{
"boost": self.get_ob_boost(ob)
}
)
{
f:self.format(ob, f) for f in self.fields
}
for ob in (o for o in self.system.allobjects.values() if o.isVisible)
]

Expand Down Expand Up @@ -160,12 +147,12 @@ def write_lunr_index(output_dir: Path, system: model.System) -> None:
"""
LunrIndexWriter(output_dir / "searchindex.json",
system=system,
fields=["name", "names", "qname"]
fields=["name", "names", "qname", "kind"]
).write()

LunrIndexWriter(output_dir / "fullsearchindex.json",
system=system,
fields=["name", "names", "qname", "docstring", "kind"]
fields=["name", "names", "qname", "kind", "docstring",]
).write()


Expand Down
101 changes: 55 additions & 46 deletions pydoctor/templatewriter/summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,16 +372,14 @@ class HelpPage(Page):
There is one page per class, module and package.
Each page present summary table(s) which feature the members of the object.

Package or Module page
~~~~~~~~~~~~~~~~~~~~~~~
**Package or Module page**

Each of these pages has two main sections consisting of:

- summary tables submodules and subpackages and the members of the module or in the ``__init__.py`` file.
- detailed descriptions of function and attribute members.

Class page
~~~~~~~~~~
**Class page**

Each class has its own separate page.
Each of these pages has three main sections consisting of:
Expand All @@ -392,82 +390,93 @@ class HelpPage(Page):

Entries in each of these sections are omitted if they are empty or not applicable.

Module Index
~~~~~~~~~~~~
**Module Index**

Provides a high level overview of the packages and modules structure.

Class Hierarchy
~~~~~~~~~~~~~~~
**Class Hierarchy**

Provides a list of classes organized by inheritance structure. Note that ``object`` is ommited.

Index Of Names
~~~~~~~~~~~~~~
**Index Of Names**

The Index contains an alphabetic index of all objects in the documentation.


Search
------

You can search for definitions of modules, packages, classes, functions, methods and attributes.
You can search for definitions of modules, packages, classes, functions, methods and attributes. The shorcut Ctrl+K (or Cmd+K on Mac) focuses the search box.

These items can be searched using part or all of the name and/or from their docstrings if "search in docstrings" is enabled.
Multiple search terms can be provided separated by whitespace.

When the search box is focused, you can use the up and down arrow keys to navigate the results,
and press enter to open the selected result.

The search is powered by `lunrjs <https://lunrjs.com/>`_.

Indexing
~~~~~~~~
**Indexing**

By default the search only matches on the name of the object.
Enable the full text search in the docstrings with the checkbox option.

You can instruct the search to look only in specific fields by passing the field name in the search like ``docstring:term``.

**Possible fields are**:
Possible fields are:

- ``name``, the name of the object (example: "MyClassAdapter" or "my_fmin_opti").
- ``qname``, the fully qualified name of the object (example: "lib.classses.MyClassAdapter").
- ``names``, the name splitted on camel case or snake case (example: "My Class Adapter" or "my fmin opti")
- ``docstring``, the docstring of the object (example: "This is an adapter for HTTP json requests that logs into a file...")
- ``kind``, can be one of: $kind_names
- ``qname``, the fully qualified name of the object (example: "lib.classses.MyClassAdapter").
- ``names``, the name splitted on camel case or snake case (example: "My Class Adapter" or "my fmin opti")

Last two fields are only applicable if "search in docstrings" is enabled.

Other search features
~~~~~~~~~~~~~~~~~~~~~
Field "docstring" is only applicable if "search in docstrings" is enabled.

Term presence.
The default behaviour is to give a better ranking to object matching multiple terms of your query,
but still show entries that matches only one of the two terms.
To change this behavour, you can use the sign ``+``.

- To indicate a term must exactly match use the plus sing: ``+``.
- To indicate a term must not match use the minus sing: ``-``.
**Term presence**

By default, multiple terms in the query are combined with logical AND:
all (non-optional) terms must match for a result to be returned.

You can change how an individual term participates in the query by
prefixing it with one of three modifiers:

- ``+term``: The '+' prefix indicates an exact/required presence for that term.
When '+' is used the automatic trailing wildcard is suppressed and the
search treats the term as an exact token match (rather than a
prefix/wildcard search). The term must be present for a result to
match.
- ``-term``: The '-' prefix marks the term as an exclusion. Matches that contain
that term are filtered out. Like '+', the '-' prefix suppresses the
automatic trailing wildcard and treats the term as an exact token to
be excluded.
- ``?term``: The '?' prefix marks the term as optional. Optional terms are not
required for a result to match; they are used to increase relevance
if present but do not enforce inclusion.


Wildcards
A trailling wildcard is automatically added to each term of your query if they don't contain an explicit term presence (``+`` or ``-``).
Searching for ``foo`` is the same as searching for ``foo*``.
**Wildcards**

- By default each plain term (without a presence modifier) gets an
automatic trailing wildcard, so "foo" is treated like "foo*".
In addition to this automatic feature, you can manually add a wildcard
anywhere else in the query.
- If a term is prefixed with '+' or '-' the automatic trailing wildcard
is not added, turning the term into an exact token match/exclusion.
- If a term contains a dot ('.'), a leading wildcard is also added to
enable matching across dotted module/class boundaries. For example,
"model." behaves like "*model.*".

If the query include a dot (``.``), a leading wildcard will to also added,
searching for ``model.`` is the same as ``*model.*`` and ``.model`` is the same as ``*.model*``.

In addition to this automatic feature, you can manually add a wildcard anywhere else in the query.


Query examples
~~~~~~~~~~~~~~

- "doc" matches "pydoctor.model.Documentable" and "pydoctor.model.DocLocation".
- "+doc" matches "pydoctor.model.DocLocation" but won't match "pydoctor.model.Documentable".
- "ensure doc" matches "pydoctor.epydoc2stan.ensure_parsed_docstring" and other object whose matches either "doc" or "ensure".
- "inp str" matches "java.io.InputStream" and other object whose matches either "in" or "str".
- "model." matches everything in the pydoctor.model module.
- ".web.*tag" matches "twisted.web.teplate.Tag" and related.
- "docstring:ansi" matches object whose docstring matches "ansi".
**Examples**

- ``doc`` -> matches names containing tokens that start with "doc" (equivalent to "doc*").
- ``ensure doc`` -> matches object whose matches "doc*" and "ensure*".
- ``doc kind:class`` -> matches classes whose matches "doc*".
- ``docstring:ansi`` -> matches object whose docstring matches "ansi*".
- ``+doc`` -> matches only where a token equals "doc" exactly.
- ``-test`` -> excludes any result containing a token equal to "test".
- ``?input ?str`` -> matches results that contain either "input" or "str" but neither is required.
- ``+doc -deprecated ?helper`` -> requires an exact "doc" token, excludes "deprecated", and treats "helper" as optional.
''')

def title(self) -> str:
Expand Down
4 changes: 4 additions & 0 deletions pydoctor/themes/base/apidocs.css
Original file line number Diff line number Diff line change
Expand Up @@ -1151,6 +1151,10 @@ input[type="search"]::-webkit-search-results-decoration { display: none; }
cursor: pointer;
}

.search-result-selected > * {
background-color: rgba(109, 161, 219, 0.2)!important;;
}

/* Constant values repr */
pre.constant-value { padding: .5em; }
.rst-variable-linewrap { color: #604000; font-weight: bold; }
Expand Down
Loading
Loading