diff --git a/build/src/php/FileGenerator/Application/ApiSearchGenerator.php b/build/src/php/FileGenerator/Application/ApiSearchGenerator.php index 0ab55a5..375a5f0 100644 --- a/build/src/php/FileGenerator/Application/ApiSearchGenerator.php +++ b/build/src/php/FileGenerator/Application/ApiSearchGenerator.php @@ -51,13 +51,19 @@ public function generateSearchIndex(): array } $result[] = [ + 'id' => 'api_' . $fn->fnName(), 'fnName' => $fn->fnName(), 'fnSignature' => $fn->fnSignature(), 'desc' => $this->formatDescription($fn->description()), 'anchor' => $anchor, + 'type' => 'api', ]; } + // Add documentation files to search index + $documentationItems = $this->generateDocumentationSearchItems(); + $result = array_merge($result, $documentationItems); + return $result; } @@ -68,4 +74,61 @@ private function formatDescription(string $desc): string { return preg_replace('/\[(.*?)\]\((.*?)\)/', '$1', $desc); } + + /** + * Generate search index items for documentation files + * + * @return array + */ + 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 @@