+ */
+ private function generateDocumentationSearchItems(): array
+ {
+ $result = [];
+ $documentationPath = __DIR__ . '/../../../../../content/documentation';
+
+ if (!is_dir($documentationPath)) {
+ error_log("Documentation path not found: " . $documentationPath);
+ return [];
+ }
+
+ $files = scandir($documentationPath);
+ if ($files === false) {
+ error_log("Could not scan documentation directory: " . $documentationPath);
+ return [];
+ }
+
+ foreach ($files as $file) {
+ if (pathinfo($file, PATHINFO_EXTENSION) !== 'md' || $file === '_index.md') {
+ continue;
+ }
+
+ $filePath = $documentationPath . '/' . $file;
+ $content = file_get_contents($filePath);
+
+ // Extract title from frontmatter
+ $title = pathinfo($file, PATHINFO_FILENAME);
+ if (preg_match('/title = "([^"]+)"/', $content, $matches)) {
+ $title = $matches[1];
+ }
+
+ // Remove frontmatter
+ $content = preg_replace('/\+\+\+.*?\+\+\+/s', '', $content);
+
+ // Remove markdown formatting and clean content
+ $content = preg_replace('/[#`*\[\]()]/', ' ', $content);
+ $content = preg_replace('/\s+/', ' ', trim($content));
+
+ // Limit content length for search index
+ $content = substr($content, 0, 500);
+
+ $result[] = [
+ 'id' => 'doc_' . pathinfo($file, PATHINFO_FILENAME),
+ 'title' => $title,
+ 'content' => $content,
+ 'url' => '/documentation/' . pathinfo($file, PATHINFO_FILENAME),
+ 'type' => 'documentation',
+ ];
+ }
+
+ return $result;
+ }
}
diff --git a/build/tests/php/FileGenerator/Domain/ApiSearchGeneratorTest.php b/build/tests/php/FileGenerator/Domain/ApiSearchGeneratorTest.php
index 0a96d6c..1acc7e1 100644
--- a/build/tests/php/FileGenerator/Domain/ApiSearchGeneratorTest.php
+++ b/build/tests/php/FileGenerator/Domain/ApiSearchGeneratorTest.php
@@ -29,14 +29,20 @@ public function test_generate_search_index_one_item(): void
$expected = [
[
+ 'id' => 'api_table?',
'fnName' => 'table?',
'fnSignature' => '(table? x)',
'desc' => 'doc for table?',
'anchor' => 'table',
+ 'type' => 'api',
],
];
- self::assertEquals($expected, $actual);
+ // Filter out documentation items for this test
+ $apiItems = array_filter($actual, fn($item) => $item['type'] === 'api');
+ $apiItems = array_values($apiItems); // Re-index array
+
+ self::assertEquals($expected, $apiItems);
}
public function test_multiple_items_in_different_groups(): void
@@ -63,20 +69,28 @@ public function test_multiple_items_in_different_groups(): void
$expected = [
[
+ 'id' => 'api_table',
'fnName' => 'table',
'fnSignature' => '(table & xs)',
'desc' => 'doc for table',
'anchor' => 'table',
+ 'type' => 'api',
],
[
+ 'id' => 'api_not',
'fnName' => 'not',
'fnSignature' => '(not x)',
'desc' => 'doc for not',
'anchor' => 'not',
+ 'type' => 'api',
],
];
- self::assertEquals($expected, $actual);
+ // Filter out documentation items for this test
+ $apiItems = array_filter($actual, fn($item) => $item['type'] === 'api');
+ $apiItems = array_values($apiItems); // Re-index array
+
+ self::assertEquals($expected, $apiItems);
}
public function test_multiple_items_in_the_same_group(): void
@@ -103,20 +117,28 @@ public function test_multiple_items_in_the_same_group(): void
$expected = [
[
+ 'id' => 'api_table',
'fnName' => 'table',
'fnSignature' => '(table & xs)',
'desc' => 'doc for table',
'anchor' => 'table',
+ 'type' => 'api',
],
[
+ 'id' => 'api_table?',
'fnName' => 'table?',
'fnSignature' => '(table? x)',
'desc' => 'doc for table?',
'anchor' => 'table-1',
+ 'type' => 'api',
],
];
- self::assertEquals($expected, $actual);
+ // Filter out documentation items for this test
+ $apiItems = array_filter($actual, fn($item) => $item['type'] === 'api');
+ $apiItems = array_values($apiItems); // Re-index array
+
+ self::assertEquals($expected, $apiItems);
}
public function test_fn_name_with_slash_in_the_middle(): void
@@ -143,20 +165,28 @@ public function test_fn_name_with_slash_in_the_middle(): void
$expected = [
[
+ 'id' => 'api_http/response',
'fnName' => 'http/response',
'fnSignature' => '',
'desc' => '',
'anchor' => 'http-response',
+ 'type' => 'api',
],
[
+ 'id' => 'api_http/response?',
'fnName' => 'http/response?',
'fnSignature' => '',
'desc' => '',
'anchor' => 'http-response-1',
+ 'type' => 'api',
],
];
- self::assertEquals($expected, $actual);
+ // Filter out documentation items for this test
+ $apiItems = array_filter($actual, fn($item) => $item['type'] === 'api');
+ $apiItems = array_values($apiItems); // Re-index array
+
+ self::assertEquals($expected, $apiItems);
}
public function test_fn_name_ending_with_minus(): void
@@ -183,20 +213,28 @@ public function test_fn_name_ending_with_minus(): void
$expected = [
[
+ 'id' => 'api_defn',
'fnName' => 'defn',
'fnSignature' => '',
'desc' => '',
'anchor' => 'defn',
+ 'type' => 'api',
],
[
+ 'id' => 'api_defn-',
'fnName' => 'defn-',
'fnSignature' => '',
'desc' => '',
'anchor' => 'defn-1',
+ 'type' => 'api',
],
];
- self::assertEquals($expected, $actual);
+ // Filter out documentation items for this test
+ $apiItems = array_filter($actual, fn($item) => $item['type'] === 'api');
+ $apiItems = array_values($apiItems); // Re-index array
+
+ self::assertEquals($expected, $apiItems);
}
public function test_fn_name_with_upper_case(): void
@@ -223,19 +261,27 @@ public function test_fn_name_with_upper_case(): void
$expected = [
[
+ 'id' => 'api_NAN',
'fnName' => 'NAN',
'fnSignature' => '',
'desc' => '',
'anchor' => 'nan',
+ 'type' => 'api',
],
[
+ 'id' => 'api_nan?',
'fnName' => 'nan?',
'fnSignature' => '',
'desc' => '',
'anchor' => 'nan-1',
+ 'type' => 'api',
],
];
- self::assertEquals($expected, $actual);
+ // Filter out documentation items for this test
+ $apiItems = array_filter($actual, fn($item) => $item['type'] === 'api');
+ $apiItems = array_values($apiItems); // Re-index array
+
+ self::assertEquals($expected, $apiItems);
}
}
diff --git a/static/search.js b/static/search.js
index 3f1bbd4..6886842 100644
--- a/static/search.js
+++ b/static/search.js
@@ -109,7 +109,9 @@ function initSearch() {
const index = elasticlunr(function () {
this.addField("fnName");
this.addField("desc");
- this.setRef("anchor");
+ this.addField("title");
+ this.addField("content");
+ this.setRef("id");
elasticlunr.stopWordFilter.stopWords = {};
elasticlunr.Pipeline.registerFunction(elasticlunr.trimmer, "trimmer");
elasticlunr.tokenizer.seperator = /[\s~~]+/;
@@ -174,10 +176,12 @@ function showResults(index) {
}
const options = {
- bool: "AND",
+ bool: "OR",
fields: {
fnName: {boost: 3},
+ title: {boost: 2},
desc: {boost: 1},
+ content: {boost: 1}
},
expand: true
};
@@ -188,6 +192,7 @@ function showResults(index) {
fnSignature: "",
desc: "Cannot provide any Phel symbol. Try something else",
anchor: "#",
+ type: "api"
};
createMenuItem(emptyResult, null);
@@ -215,11 +220,22 @@ function createMenuItem(result, index) {
}
function formatSearchResultItem(item) {
- return ``
- + `${item.fnName} `
- + `${item.fnSignature}`
- + `${item.desc}`
- + `
`;
+ if (item.type === "documentation") {
+ return ``
+ + ``
+ + `Documentation: `
+ + `${item.title}`
+ + `${item.content}`
+ + `
`;
+ } else {
+ return ``
+ + ``
+ + `API: `
+ + `${item.fnName} `
+ + `${item.fnSignature}`
+ + `${item.desc}`
+ + `
`;
+ }
}
function removeSelectedClassFromSearchResult() {
diff --git a/templates/header.html b/templates/header.html
index 45ae37b..bf83a91 100644
--- a/templates/header.html
+++ b/templates/header.html
@@ -21,7 +21,7 @@