Skip to content

Commit

Permalink
Merge pull request #24 from jbms/fix-sphinx43-search-index-compatibility
Browse files Browse the repository at this point in the history
Fixes sphinx_search.ts to work with the Sphinx 4.3 search index format
  • Loading branch information
jbms authored Nov 12, 2021
2 parents 1511a1b + e769608 commit 191bc36
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 47 deletions.
3 changes: 2 additions & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ disable=raw-checker-failed,
no-self-use,
no-member,
duplicate-code,
too-many-statements
too-many-statements,
too-many-locals

# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
Expand Down
12 changes: 6 additions & 6 deletions sphinx_immaterial/search_adapt.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@ def get_objects(
]:
rv = super().get_objects(fn2index)
onames = self._objnames
for prefix in rv:
for prefix, prefix_value in rv.items():
if prefix:
name_prefix = prefix + "."
else:
name_prefix = ""
if sphinx.version_info >= (4, 3):
# From sphinx 4.3 onwards the children dict is now a list
children = rv[prefix]
children = prefix_value
else:
children = [(*values, name) for name, values in rv[prefix].items()]
children = [(*values, name) for name, values in prefix_value.items()]
for i, (docindex, typeindex, prio, shortanchor, name) in enumerate(
children
):
Expand All @@ -54,16 +54,16 @@ def get_objects(
if synopsis:
synopsis = synopsis.strip()
if sphinx.version_info >= (4, 3):
rv[prefix][i] = (
prefix_value[i] = (
docindex,
typeindex,
prio,
shortanchor,
synopsis,
name,
synopsis,
)
else:
rv[prefix][name] = (
prefix_value[name] = (
docindex,
typeindex,
prio,
Expand Down
103 changes: 63 additions & 40 deletions src/assets/javascripts/sphinx_search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,13 @@ const Scorer = {
partialTerm: 2
}

// The representation of the list of objects changes in Sphinx 4.3.
type Sphinx42Objects = Record<string, [documentIndex: number, objectTypeIndex: number, priority: number, anchor: string, synopsis: string]>
type Sphinx43Objects = [documentIndex: number, objectTypeIndex: number, priority: number, anchor: string, name: string, synopsis: string][]

interface SearchIndex {
docurls: string[]
objects: Record<string, Record<string, [documentIndex: number, objectTypeIndex: number, priority: number, anchor: string, synopsis: string]>>
objects: Record<string, Sphinx42Objects|Sphinx43Objects>
objnames: Record<number | string, string[]>
objtypes: Record<number | string, string>
terms: Record<string, number | number[]>
Expand Down Expand Up @@ -142,54 +146,73 @@ function performObjectSearch(objectTerm: string, otherterms: string[]): SphinxSe
const {docurls, objects, objnames, titles} = searchIndex

const results: SphinxSearchResult[] = []
for (const prefix in objects) {
for (const name in objects[prefix]) {
const fullname = (prefix ? `${prefix}.` : "") + name
const fullnameLower = fullname.toLowerCase()
if (fullnameLower.indexOf(objectTerm) > -1) {
let score = 0
const parts = fullnameLower.split(".")
// check for different match types: exact matches of full name or
// "last name" (i.e. last dotted part)
if (fullnameLower === objectTerm || parts[parts.length - 1] === objectTerm) {
score += Scorer.objNameMatch
// matches in last name
} else if (parts[parts.length - 1].indexOf(objectTerm) > -1) {
score += Scorer.objPartialMatch
}
const match = objects[prefix][name]
const objname = objnames[match[1]][2]
const title = titles[match[0]]
const synopsis = match[4]
// If more than one term searched for, we require other words to be
// found in the name/title/description
if (otherterms.length > 0) {
const haystack = `${prefix} ${name} ${objname} ${title} ${synopsis}`.toLowerCase()
let allfound = true
for (let i = 0; i < otherterms.length; i++) {
if (haystack.indexOf(otherterms[i]) === -1) {
allfound = false
break
}
}
if (!allfound) {
continue

/**
* @param prefix - Prefix of object name, e.g. `modulename` or `modulename.ClassName`.
* @param docindex - Index into `docurls` and `titles`.
* @param typeindex - Index into `objnames`.
* @param prio - Index into `Scorer.objPrior`.
* @param anchor - Anchor within page.
* @param name - Final component of object name.
* @param synopsis - Object synopsis.
*/
function matchEntry(prefix: string, docindex: number, typeindex: number, prio: number, anchor: string, name: string, synopsis: string) {
const fullname = (prefix ? `${prefix}.` : "") + name
const fullnameLower = fullname.toLowerCase()
if (fullnameLower.indexOf(objectTerm) > -1) {
let score = 0
const parts = fullnameLower.split(".")
// check for different match types: exact matches of full name or
// "last name" (i.e. last dotted part)
if (fullnameLower === objectTerm || parts[parts.length - 1] === objectTerm) {
score += Scorer.objNameMatch
// matches in last name
} else if (parts[parts.length - 1].indexOf(objectTerm) > -1) {
score += Scorer.objPartialMatch
}
const objname = objnames[typeindex][2]
const title = titles[docindex]
// If more than one term searched for, we require other words to be
// found in the name/title/description
if (otherterms.length > 0) {
const haystack = `${prefix} ${name} ${objname} ${title} ${synopsis ?? ""}`.toLowerCase()
let allfound = true
for (let i = 0; i < otherterms.length; i++) {
if (haystack.indexOf(otherterms[i]) === -1) {
allfound = false
break
}
}
if (!allfound) {
return
}
}

let anchor = match[3]
if (anchor === "") anchor = fullname
else if (anchor === "-") anchor = `${objnames[match[1]][1]}-${fullname}`
// add custom score for some objects according to scorer
score += Scorer.objPrio[match[2]] ?? Scorer.objPrioDefault
results.push({
docurl: docurls[match[0]],
if (anchor === "") anchor = fullname
else if (anchor === "-") anchor = `${objnames[typeindex][1]}-${fullname}`
// add custom score for some objects according to scorer
score += Scorer.objPrio[prio] ?? Scorer.objPrioDefault
results.push({
docurl: docurls[docindex],
title: fullname,
anchor: `#${anchor}`,
objectLabel: objname,
synopsis,
score
})
}
}
for (const prefix in objects) {
const entries = objects[prefix]
if (Array.isArray(entries)) {
for (const entry of entries) {
const [docindex, typeindex, prio, anchor, name, synopsis] = entry
matchEntry(prefix, docindex, typeindex, prio, anchor, name, synopsis)
}
} else {
for (const name in entries) {
const [docindex, typeindex, prio, anchor, synopsis] = entries[name]
matchEntry(prefix, docindex, typeindex, prio, anchor, name, synopsis)
}
}
}
Expand Down

0 comments on commit 191bc36

Please sign in to comment.