Skip to content

Commit

Permalink
- Changed LunrJs to MinisearchJs for client-side search
Browse files Browse the repository at this point in the history
  • Loading branch information
Hetarth02 committed Jul 5, 2023
1 parent 251e90d commit b858db5
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 120 deletions.
241 changes: 123 additions & 118 deletions assets/html/search.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,68 @@
// libraries: jquery, lunr, lodash
// arguments: $, lunr, _
// libraries: jquery, minisearch, lodash
// arguments: $, minisearch, _

$(document).ready(function() {
$(function () {
// parseUri 1.2.2
// (c) Steven Levithan <stevenlevithan.com>
// MIT License
function parseUri (str) {
var o = parseUri.options,
m = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
function parseUri(str) {
var o = parseUri.options,
m = o.parser[o.strictMode ? 'strict' : 'loose'].exec(str),
uri = {},
i = 14;
i = 14

while (i--) uri[o.key[i]] = m[i] || "";
while (i--) uri[o.key[i]] = m[i] || ''

uri[o.q.name] = {};
uri[o.q.name] = {}
uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
if ($1) uri[o.q.name][$1] = $2;
});
if ($1) uri[o.q.name][$1] = $2
})

return uri;
};
return uri
}
parseUri.options = {
strictMode: false,
key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
q: {
name: "queryKey",
parser: /(?:^|&)([^&=]*)=?([^&]*)/g
key: [
'source',
'protocol',
'authority',
'userInfo',
'user',
'password',
'host',
'port',
'relative',
'path',
'directory',
'file',
'query',
'anchor',
],
q: {
name: 'queryKey',
parser: /(?:^|&)([^&=]*)=?([^&]*)/g,
},
parser: {
strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
}
};
strict:
/^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
loose:
/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,
},
}

$("#search-form").submit(function(e) {
$('#search-form').submit(function (e) {
e.preventDefault()
})

let ms_data = documenterSearchIndex['docs'].map((x, key) => {
x['id'] = key
return x
})

// list below is the lunr 2.1.3 list minus the intersect with names(Base)
// (all, any, get, in, is, only, which) and (do, else, for, let, where, while, with)
// ideally we'd just filter the original list but it's not available as a variable
lunr.stopWordFilter = lunr.generateStopWordFilter([
const stopWords = new Set([
'a',
'able',
'about',
Expand Down Expand Up @@ -145,113 +167,96 @@ $(document).ready(function() {
'would',
'yet',
'you',
'your'
'your',
])

// add . as a separator, because otherwise "title": "Documenter.Anchors.add!"
// would not find anything if searching for "add!", only for the entire qualification
lunr.tokenizer.separator = /[\s\-\.]+/

// custom trimmer that doesn't strip @ and !, which are used in julia macro and function names
lunr.trimmer = function (token) {
return token.update(function (s) {
return s.replace(/^[^a-zA-Z0-9@!]+/, '').replace(/[^a-zA-Z0-9@!]+$/, '')
})
}
let index = new minisearch({
fields: ['title', 'text'], // fields to index for full-text search
storeFields: ['location', 'title', 'text', 'category', 'page'], // fields to return with search results
processTerm: (term) => {
let word = stopWords.has(term) ? null : term
if (word) {
// custom trimmer that doesn't strip @ and !, which are used in julia macro and function names
word = word.replace(/^[^a-zA-Z0-9@!]+/, '').replace(/[^a-zA-Z0-9@!]+$/, '')
}

lunr.Pipeline.registerFunction(lunr.stopWordFilter, 'juliaStopWordFilter')
lunr.Pipeline.registerFunction(lunr.trimmer, 'juliaTrimmer')
return word ?? null
},
// add . as a separator, because otherwise "title": "Documenter.Anchors.add!", would not find anything if searching for "add!", only for the entire qualification
tokenize: (string) => string.split(/[\s\-\.]+/),
searchOptions: {
boost: { title: 100 },
fuzzy: 2,
processTerm: (term) => {
let word = stopWords.has(term) ? null : term
if (word) {
word = word.replace(/^[^a-zA-Z0-9@!]+/, '').replace(/[^a-zA-Z0-9@!]+$/, '')
}

var index = lunr(function () {
this.ref('location')
this.field('title',{boost: 100})
this.field('text')
documenterSearchIndex['docs'].forEach(function(e) {
this.add(e)
}, this)
return word ?? null
},
tokenize: (string) => string.split(/[\s\-\.]+/),
},
})
var store = {}

documenterSearchIndex['docs'].forEach(function(e) {
store[e.location] = {title: e.title, category: e.category, page: e.page}
})
index.addAll(ms_data)

$(function(){
searchresults = $('#documenter-search-results');
searchinfo = $('#documenter-search-info');
searchbox = $('#documenter-search-query');
searchform = $('.docs-search');
sidebar = $('.docs-sidebar');
function update_search(querystring) {
tokens = lunr.tokenizer(querystring)
results = index.query(function (q) {
tokens.forEach(function (t) {
q.term(t.toString(), {
fields: ["title"],
boost: 100,
usePipeline: true,
editDistance: 0,
wildcard: lunr.Query.wildcard.NONE
})
q.term(t.toString(), {
fields: ["title"],
boost: 10,
usePipeline: true,
editDistance: 2,
wildcard: lunr.Query.wildcard.NONE
})
q.term(t.toString(), {
fields: ["text"],
boost: 1,
usePipeline: true,
editDistance: 0,
wildcard: lunr.Query.wildcard.NONE
})
})
})
searchinfo.text("Number of results: " + results.length)
searchresults.empty()
results.forEach(function(result) {
data = store[result.ref]
link = $('<a class="docs-label">'+data.title+'</a>')
link.attr('href', documenterBaseURL+'/'+result.ref)
if (data.category != "page"){
cat = $('<span class="docs-category">('+data.category+', '+data.page+')</span>')
} else {
cat = $('<span class="docs-category">('+data.category+')</span>')
}
li = $('<li>').append(link).append(" ").append(cat)
searchresults.append(li)
})
}
searchresults = $('#documenter-search-results')
searchinfo = $('#documenter-search-info')
searchbox = $('#documenter-search-query')
searchform = $('.docs-search')
sidebar = $('.docs-sidebar')

function update_search_box() {
querystring = searchbox.val()
update_search(querystring)
}
function update_search(querystring) {
let results = []
results = index.search(querystring, {
filter: (result) => result.score >= 1,
})

searchbox.keyup(_.debounce(update_search_box, 250))
searchbox.change(update_search_box)
searchinfo.text('Number of results: ' + results.length)
searchresults.empty()

// Disable enter-key form submission for the searchbox on the search page
// and just re-run search rather than refresh the whole page.
searchform.keypress(
function(event){
if (event.which == '13') {
if (sidebar.hasClass('visible')) {
sidebar.removeClass('visible');
}
update_search_box();
event.preventDefault();
}
results.forEach(function (result) {
let link = $('<a class="docs-label">' + result.title + '</a>')
link.attr('href', documenterBaseURL + '/' + result.location)

let cat = ''
if (result.category != 'page') {
cat = $('<span class="docs-category">(' + result.category + ', ' + result.page + ')</span>')
} else {
cat = $('<span class="docs-category">(' + result.category + ')</span>')
}
);
let li = $('<li>').append(link).append(' ').append(cat)
searchresults.append(li)
})
}

search_query_uri = parseUri(window.location).queryKey["q"]
if(search_query_uri !== undefined) {
search_query = decodeURIComponent(search_query_uri.replace(/\+/g, '%20'))
searchbox.val(search_query)
function update_search_box() {
querystring = searchbox.val()
update_search(querystring)
}

searchbox.keyup(_.debounce(update_search_box, 250))
searchbox.change(update_search_box)

// Disable enter-key form submission for the searchbox on the search page
// and just re-run search rather than refresh the whole page.
searchform.keypress(function (event) {
if (event.which == '13') {
if (sidebar.hasClass('visible')) {
sidebar.removeClass('visible')
}
update_search_box()
event.preventDefault()
}
update_search_box();
})

search_query_uri = parseUri(window.location).queryKey['q']

if (search_query_uri !== undefined) {
search_query = decodeURIComponent(search_query_uri.replace(/\+/g, '%20'))
searchbox.val(search_query)
}

update_search_box()
})
2 changes: 1 addition & 1 deletion src/html/HTMLWriter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ function render(doc::Documenter.Document, settings::HTML=HTML())
if isfile(joinpath(doc.user.source, "assets", "search.js"))
@warn "not creating 'search.js', provided by the user."
else
r = JSDependencies.RequireJS([RD.jquery, RD.lunr, RD.lodash])
r = JSDependencies.RequireJS([RD.jquery, RD.minisearch, RD.lodash])
push!(r, JSDependencies.parse_snippet(joinpath(ASSETS, "search.js")))
JSDependencies.verify(r; verbose=true) || error("RequireJS declaration is invalid")
JSDependencies.writejs(joinpath(doc.user.build, "assets", "search.js"), r)
Expand Down
2 changes: 1 addition & 1 deletion src/html/RD.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ module RD

const jquery = RemoteLibrary("jquery", "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.4/jquery.min.js")
const jqueryui = RemoteLibrary("jqueryui", "https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.13.2/jquery-ui.min.js")
const lunr = RemoteLibrary("lunr", "https://cdnjs.cloudflare.com/ajax/libs/lunr.js/2.3.9/lunr.min.js")
const lodash = RemoteLibrary("lodash", "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js")
const minisearch = RemoteLibrary("minisearch", "https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/index.min.js")

# headroom
const headroom_version = "0.12.0"
Expand Down

0 comments on commit b858db5

Please sign in to comment.