Skip to content

Commit a7956ba

Browse files
committed
Merge branch 'master' into improve-search-ux
2 parents 0c774cf + 68452e0 commit a7956ba

File tree

8 files changed

+180
-20
lines changed

8 files changed

+180
-20
lines changed

build/src/php/FileGenerator/Application/ApiSearchGenerator.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,19 @@ public function generateSearchIndex(): array
5151
}
5252

5353
$result[] = [
54+
'id' => 'api_' . $fn->fnName(),
5455
'fnName' => $fn->fnName(),
5556
'fnSignature' => $fn->fnSignature(),
5657
'desc' => $this->formatDescription($fn->description()),
5758
'anchor' => $anchor,
59+
'type' => 'api',
5860
];
5961
}
6062

63+
// Add documentation files to search index
64+
$documentationItems = $this->generateDocumentationSearchItems();
65+
$result = array_merge($result, $documentationItems);
66+
6167
return $result;
6268
}
6369

@@ -68,4 +74,61 @@ private function formatDescription(string $desc): string
6874
{
6975
return preg_replace('/\[(.*?)\]\((.*?)\)/', '<i>$1</i>', $desc);
7076
}
77+
78+
/**
79+
* Generate search index items for documentation files
80+
*
81+
* @return array<array{id: string, title: string, content: string, url: string, type: string}>
82+
*/
83+
private function generateDocumentationSearchItems(): array
84+
{
85+
$result = [];
86+
$documentationPath = __DIR__ . '/../../../../../content/documentation';
87+
88+
if (!is_dir($documentationPath)) {
89+
error_log("Documentation path not found: " . $documentationPath);
90+
return [];
91+
}
92+
93+
$files = scandir($documentationPath);
94+
if ($files === false) {
95+
error_log("Could not scan documentation directory: " . $documentationPath);
96+
return [];
97+
}
98+
99+
foreach ($files as $file) {
100+
if (pathinfo($file, PATHINFO_EXTENSION) !== 'md' || $file === '_index.md') {
101+
continue;
102+
}
103+
104+
$filePath = $documentationPath . '/' . $file;
105+
$content = file_get_contents($filePath);
106+
107+
// Extract title from frontmatter
108+
$title = pathinfo($file, PATHINFO_FILENAME);
109+
if (preg_match('/title = "([^"]+)"/', $content, $matches)) {
110+
$title = $matches[1];
111+
}
112+
113+
// Remove frontmatter
114+
$content = preg_replace('/\+\+\+.*?\+\+\+/s', '', $content);
115+
116+
// Remove markdown formatting and clean content
117+
$content = preg_replace('/[#`*\[\]()]/', ' ', $content);
118+
$content = preg_replace('/\s+/', ' ', trim($content));
119+
120+
// Limit content length for search index
121+
$content = substr($content, 0, 500);
122+
123+
$result[] = [
124+
'id' => 'doc_' . pathinfo($file, PATHINFO_FILENAME),
125+
'title' => $title,
126+
'content' => $content,
127+
'url' => '/documentation/' . pathinfo($file, PATHINFO_FILENAME),
128+
'type' => 'documentation',
129+
];
130+
}
131+
132+
return $result;
133+
}
71134
}

build/tests/php/FileGenerator/Domain/ApiSearchGeneratorTest.php

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,20 @@ public function test_generate_search_index_one_item(): void
2929

3030
$expected = [
3131
[
32+
'id' => 'api_table?',
3233
'fnName' => 'table?',
3334
'fnSignature' => '(table? x)',
3435
'desc' => 'doc for table?',
3536
'anchor' => 'table',
37+
'type' => 'api',
3638
],
3739
];
3840

39-
self::assertEquals($expected, $actual);
41+
// Filter out documentation items for this test
42+
$apiItems = array_filter($actual, fn($item) => $item['type'] === 'api');
43+
$apiItems = array_values($apiItems); // Re-index array
44+
45+
self::assertEquals($expected, $apiItems);
4046
}
4147

4248
public function test_multiple_items_in_different_groups(): void
@@ -63,20 +69,28 @@ public function test_multiple_items_in_different_groups(): void
6369

6470
$expected = [
6571
[
72+
'id' => 'api_table',
6673
'fnName' => 'table',
6774
'fnSignature' => '(table & xs)',
6875
'desc' => 'doc for table',
6976
'anchor' => 'table',
77+
'type' => 'api',
7078
],
7179
[
80+
'id' => 'api_not',
7281
'fnName' => 'not',
7382
'fnSignature' => '(not x)',
7483
'desc' => 'doc for not',
7584
'anchor' => 'not',
85+
'type' => 'api',
7686
],
7787
];
7888

79-
self::assertEquals($expected, $actual);
89+
// Filter out documentation items for this test
90+
$apiItems = array_filter($actual, fn($item) => $item['type'] === 'api');
91+
$apiItems = array_values($apiItems); // Re-index array
92+
93+
self::assertEquals($expected, $apiItems);
8094
}
8195

8296
public function test_multiple_items_in_the_same_group(): void
@@ -103,20 +117,28 @@ public function test_multiple_items_in_the_same_group(): void
103117

104118
$expected = [
105119
[
120+
'id' => 'api_table',
106121
'fnName' => 'table',
107122
'fnSignature' => '(table & xs)',
108123
'desc' => 'doc for table',
109124
'anchor' => 'table',
125+
'type' => 'api',
110126
],
111127
[
128+
'id' => 'api_table?',
112129
'fnName' => 'table?',
113130
'fnSignature' => '(table? x)',
114131
'desc' => 'doc for table?',
115132
'anchor' => 'table-1',
133+
'type' => 'api',
116134
],
117135
];
118136

119-
self::assertEquals($expected, $actual);
137+
// Filter out documentation items for this test
138+
$apiItems = array_filter($actual, fn($item) => $item['type'] === 'api');
139+
$apiItems = array_values($apiItems); // Re-index array
140+
141+
self::assertEquals($expected, $apiItems);
120142
}
121143

122144
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
143165

144166
$expected = [
145167
[
168+
'id' => 'api_http/response',
146169
'fnName' => 'http/response',
147170
'fnSignature' => '',
148171
'desc' => '',
149172
'anchor' => 'http-response',
173+
'type' => 'api',
150174
],
151175
[
176+
'id' => 'api_http/response?',
152177
'fnName' => 'http/response?',
153178
'fnSignature' => '',
154179
'desc' => '',
155180
'anchor' => 'http-response-1',
181+
'type' => 'api',
156182
],
157183
];
158184

159-
self::assertEquals($expected, $actual);
185+
// Filter out documentation items for this test
186+
$apiItems = array_filter($actual, fn($item) => $item['type'] === 'api');
187+
$apiItems = array_values($apiItems); // Re-index array
188+
189+
self::assertEquals($expected, $apiItems);
160190
}
161191

162192
public function test_fn_name_ending_with_minus(): void
@@ -183,20 +213,28 @@ public function test_fn_name_ending_with_minus(): void
183213

184214
$expected = [
185215
[
216+
'id' => 'api_defn',
186217
'fnName' => 'defn',
187218
'fnSignature' => '',
188219
'desc' => '',
189220
'anchor' => 'defn',
221+
'type' => 'api',
190222
],
191223
[
224+
'id' => 'api_defn-',
192225
'fnName' => 'defn-',
193226
'fnSignature' => '',
194227
'desc' => '',
195228
'anchor' => 'defn-1',
229+
'type' => 'api',
196230
],
197231
];
198232

199-
self::assertEquals($expected, $actual);
233+
// Filter out documentation items for this test
234+
$apiItems = array_filter($actual, fn($item) => $item['type'] === 'api');
235+
$apiItems = array_values($apiItems); // Re-index array
236+
237+
self::assertEquals($expected, $apiItems);
200238
}
201239

202240
public function test_fn_name_with_upper_case(): void
@@ -223,19 +261,27 @@ public function test_fn_name_with_upper_case(): void
223261

224262
$expected = [
225263
[
264+
'id' => 'api_NAN',
226265
'fnName' => 'NAN',
227266
'fnSignature' => '',
228267
'desc' => '',
229268
'anchor' => 'nan',
269+
'type' => 'api',
230270
],
231271
[
272+
'id' => 'api_nan?',
232273
'fnName' => 'nan?',
233274
'fnSignature' => '',
234275
'desc' => '',
235276
'anchor' => 'nan-1',
277+
'type' => 'api',
236278
],
237279
];
238280

239-
self::assertEquals($expected, $actual);
281+
// Filter out documentation items for this test
282+
$apiItems = array_filter($actual, fn($item) => $item['type'] === 'api');
283+
$apiItems = array_values($apiItems); // Re-index array
284+
285+
self::assertEquals($expected, $apiItems);
240286
}
241287
}

content/documentation/basic-types.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,20 @@ A set is a sequence of whitespace-separated values prefixed by the function `set
140140

141141
## Comments
142142

143-
A comment begins with a `#` character and continues until the end of the line. There are no multi-line comments.
143+
A comment begins with a `#` or `;` character and continues until the end of the line. There are no multi-line comments.
144144

145145
```phel
146146
# This is a comment
147+
; This is also a comment
147148
```
149+
150+
Phel also supports inline s-expression commenting with `#_` which comments out the next form. It can also be stacked to comment out two or more forms after it.
151+
152+
```phel
153+
[:one :two :three] # results to [:one :two :three]
154+
[#_:one :two :three] # results to [:two :three]
155+
[#_:one :two #_:three] # results to [:two]
156+
[#_#_:one :two :three] # results to [:three]
157+
```
158+
159+
See also the [comment](/documentation/api/#comment) macro which ignores the forms inside and returns `nil` while still requiring the content to be valid Phel code.

content/documentation/control-flow.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ Evaluates the expressions in order and returns the value of the last expression.
174174

175175
# Dofor
176176

177-
```
177+
```phel
178178
(dofor [x :in [1 2 3]] (print x)) # Prints 1, 2, 3 and returns nil
179179
(dofor [x :in [2 3 4 5] :when (even? x)] (print x)) # Prints 1, 2 and returns nil
180180
```

content/documentation/functions-and-recursion.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,18 @@ weight = 7
77

88
```phel
99
(fn [params*] expr*)
10+
11+
(fn
12+
([params1*] expr1*)
13+
([params2*] expr2*)
14+
...)
1015
```
1116

1217
Defines a function. A function consists of a list of parameters and a list of expression. The value of the last expression is returned as the result of the function. All other expression are only evaluated for side effects. If no expression is given, the function returns `nil`.
1318

14-
Function also introduce a new lexical scope that is not accessible outside the function.
19+
Functions can define multiple arities. When calling such a function, the clause matching the number of provided arguments is chosen. Variadic clauses are supported for at most one arity and must be the one with the most parameters. If no arity matches, a readable compile-time or runtime error is thrown.
20+
21+
Function also introduces a new lexical scope that is not accessible outside the function.
1522

1623
```phel
1724
(fn []) # Function with no arguments that returns nil
@@ -24,7 +31,13 @@ Function can also be defined as variadic function with an infinite amount of arg
2431

2532
```phel
2633
(fn [& args] (count args)) # A variadic function that counts the arguments
34+
2735
(fn [a b c &]) # A variadic function with extra arguments ignored
36+
37+
(fn # A multi-arity function
38+
([] "hi")
39+
([name] (str "hi " name))
40+
([greeting name & rest] (str greeting " " name rest)))
2841
```
2942

3043
There is a shorter form to define an anonymous function. This omits the parameter list and names parameters based on their position.
@@ -44,13 +57,23 @@ There is a shorter form to define an anonymous function. This omits the paramete
4457

4558
```phel
4659
(defn name docstring? attributes? [params*] expr*)
60+
61+
(defn name docstring? attributes?
62+
([params1*] expr1*)
63+
([params2*] expr2*)
64+
...)
4765
```
4866

49-
Global functions can be defined using `defn`.
67+
Global functions can be defined using `defn`. Like anonymous functions, they may provide multiple arities. The most specific clause based on the number of arguments is chosen at call time. A single variadic clause is allowed and must declare the maximum number of arguments.
5068

5169
```phel
5270
(defn my-add-function [a b]
5371
(+ a b))
72+
73+
(defn greet
74+
([] "hi")
75+
([name] (str "hi " name))
76+
([greeting name] (str greeting " " name)))
5477
```
5578

5679
Each global function can take an optional doc comment and attribute map.

0 commit comments

Comments
 (0)