1use super::MODULE_NAME;
3use crate::{
4 DefaultValue, FileId, ObjectField, PackageDeclaration, Param, Span, Type, ValueKind,
5 ValueSymbol,
6};
7
8pub fn package_declaration() -> PackageDeclaration {
9 let mut defs = PackageDeclaration::with_package(MODULE_NAME);
10 let line = object(&[("line", Type::Number), ("text", Type::String)], false);
11 let diagnostic = object(
12 &[
13 ("hunk", Type::Number),
14 ("line", Type::Number),
15 ("message", Type::String),
16 ],
17 false,
18 );
19 let mutation = object(
20 &[
21 ("success", Type::Boolean),
22 ("changed", Type::Boolean),
23 ("diff", Type::String),
24 ("diagnostics", array(diagnostic)),
25 ],
26 false,
27 );
28 insert(
29 &mut defs,
30 "read",
31 vec![
32 string("path"),
33 number("offset", 1.0),
34 number("limit", 200.0),
35 ],
36 object(
37 &[
38 ("path", Type::String),
39 ("lines", array(line.clone())),
40 ("truncated", Type::Boolean),
41 ],
42 false,
43 ),
44 "/** Numbered UTF-8 lines; one-based offset, default 200 lines. Requires fs.read. */",
45 );
46 let options = object(
47 &[
48 ("path", Type::String),
49 ("include", array(Type::String)),
50 ("exclude", array(Type::String)),
51 ("caseSensitive", Type::Boolean),
52 ("context", Type::Number),
53 ("mode", Type::String),
54 ("limit", Type::Number),
55 ],
56 true,
57 );
58 let hit = object(
59 &[
60 ("path", Type::String),
61 ("line", Type::Number),
62 ("text", Type::String),
63 ("before", array(line.clone())),
64 ("after", array(line)),
65 ],
66 false,
67 );
68 let count = object(&[("path", Type::String), ("count", Type::Number)], false);
69 insert(
70 &mut defs,
71 "search",
72 vec![
73 string("pattern"),
74 Param::with_default(
75 "options",
76 Type::union(vec![options, Type::Undefined]),
77 DefaultValue::Undefined,
78 ),
79 ],
80 object(
81 &[
82 ("matches", array(hit)),
83 ("files", array(Type::String)),
84 ("counts", array(count)),
85 ("truncated", Type::Boolean),
86 ],
87 false,
88 ),
89 "/** Regex search, ignoring hidden and ignored paths. Modes: matches (default), files, counts. Root defaults to /; context 0; caseSensitive true; limit 1000. Requires fs.list, fs.stat and fs.read, including ignore files; those above the root are skipped where fs.read is denied. */",
90 );
91 let entry = object(
92 &[
93 ("path", Type::String),
94 ("kind", Type::String),
95 ("depth", Type::Number),
96 ("modifiedAt", Type::Number),
97 ],
98 false,
99 );
100 let listing = object(
101 &[("entries", array(entry)), ("truncated", Type::Boolean)],
102 false,
103 );
104 insert(
105 &mut defs,
106 "glob",
107 vec![string("pattern")],
108 listing.clone(),
109 "/** Match file paths relative to /; newest modification first, path breaks ties. Maximum 1000 results. Requires fs.list, fs.stat and fs.read for ignore files. */",
110 );
111 insert(
112 &mut defs,
113 "tree",
114 vec![string("path"), number("depth", 3.0)],
115 listing,
116 "/** Ignored/hidden paths omitted; symlinks listed but never traversed. Depth defaults to 3; maximum 1000 entries. Requires fs.list, fs.stat and fs.read for ignore files; those above the root are skipped where fs.read is denied. */",
117 );
118 insert(
119 &mut defs,
120 "edit",
121 vec![
122 string("path"),
123 string("oldString"),
124 string("newString"),
125 Param::with_default("replaceAll", Type::Boolean, DefaultValue::Boolean(false)),
126 number("nearLine", 0.0),
127 ],
128 mutation.clone(),
129 "/** Replace a unique exact anchor; replaceAll selects every occurrence. nearLine chooses the uniquely nearest anchor (0 means absent). Whitespace-only whole-line near matches may preserve indentation. Requires fs.read and fs.write. */",
130 );
131 insert(
132 &mut defs,
133 "insertAt",
134 vec![
135 string("path"),
136 Param::new("line", Type::Number),
137 string("text"),
138 ],
139 mutation.clone(),
140 "/** Insert text literally before a one-based line; one past the last line appends. Requires fs.read and fs.write. */",
141 );
142 insert(
143 &mut defs,
144 "diffText",
145 vec![string("a"), string("b")],
146 Type::String,
147 "/** Pure UTF-16 text comparison; unified diff with three context lines. No capability needed. */",
148 );
149 insert(
150 &mut defs,
151 "diffFiles",
152 vec![string("a"), string("b")],
153 Type::String,
154 "/** Unified diff of two strict UTF-8 workspace files. Requires fs.read on both. */",
155 );
156 insert(
157 &mut defs,
158 "applyPatch",
159 vec![string("path"), string("patch")],
160 mutation,
161 "/** Apply a single-file unified patch by unique context, ignoring header positions. All hunks must succeed; otherwise nothing is written. Requires fs.read and fs.write. */",
162 );
163 defs
164}
165fn object(fields: &[(&str, Type)], optional: bool) -> Type {
166 Type::Object {
167 index: None,
168 fields: fields
169 .iter()
170 .map(|(name, ty)| {
171 (
172 (*name).into(),
173 if optional {
174 ObjectField::optional(ty.clone())
175 } else {
176 ObjectField::required(ty.clone())
177 },
178 )
179 })
180 .collect(),
181 }
182}
183fn array(ty: Type) -> Type {
184 Type::Array(Box::new(ty))
185}
186fn string(name: &str) -> Param {
187 Param::new(name, Type::String)
188}
189fn number(name: &str, value: f64) -> Param {
190 Param::with_default(name, Type::Number, DefaultValue::Number(value))
191}
192fn insert(defs: &mut PackageDeclaration, name: &str, params: Vec<Param>, ret: Type, doc: &str) {
193 defs.values.insert(
194 name.into(),
195 ValueSymbol {
196 name: name.into(),
197 mangled_name: crate::mangle::package_symbol(MODULE_NAME, name),
198 declaration_span: Span::at(FileId::CODE),
199 kind: ValueKind::Function {
200 generics: vec![],
201 params,
202 ret,
203 type_predicate: None,
204 doc: crate::doc(FileId::CODE, doc),
205 },
206 },
207 );
208}