Skip to content

Commit 83a1f76

Browse files
much simpler
1 parent d4e89ae commit 83a1f76

2 files changed

Lines changed: 24 additions & 236 deletions

File tree

crates/pgls_hover/src/hoverables/table.rs

Lines changed: 23 additions & 235 deletions
Original file line numberDiff line numberDiff line change
@@ -114,255 +114,39 @@ impl ToHoverMarkdown for Table {
114114
}
115115

116116
fn extract_basic_default_literal(default_expr: &str) -> Option<String> {
117-
let mut candidate = default_expr.trim();
117+
let mut cast_parts = default_expr.split("::");
118+
let mut value = cast_parts.next().unwrap_or(default_expr).trim();
118119

119-
loop {
120-
let mut changed = false;
121-
122-
if let Some(unwrapped) = strip_outer_parentheses(candidate) {
123-
candidate = unwrapped.trim();
124-
changed = true;
125-
}
126-
127-
if let Some(without_cast) = strip_trailing_top_level_casts(candidate) {
128-
candidate = without_cast.trim();
129-
changed = true;
130-
}
131-
132-
if !changed {
133-
break;
134-
}
135-
}
136-
137-
if is_basic_literal(candidate) {
138-
Some(candidate.to_string())
139-
} else {
140-
None
141-
}
142-
}
143-
144-
fn strip_outer_parentheses(value: &str) -> Option<&str> {
145-
let value = value.trim();
146-
147-
if !value.starts_with('(') || !value.ends_with(')') {
120+
if cast_parts.any(|cast_part| !is_type_cast_fragment(cast_part)) {
148121
return None;
149122
}
150123

151-
let mut depth = 0_i32;
152-
let mut in_single_quote = false;
153-
let mut in_double_quote = false;
154-
let bytes = value.as_bytes();
155-
let mut idx = 0_usize;
156-
157-
while idx < bytes.len() {
158-
let ch = bytes[idx] as char;
159-
160-
if in_single_quote {
161-
if ch == '\'' {
162-
if idx + 1 < bytes.len() && bytes[idx + 1] as char == '\'' {
163-
idx += 2;
164-
continue;
165-
}
166-
167-
in_single_quote = false;
168-
}
169-
170-
idx += 1;
171-
continue;
172-
}
173-
174-
if in_double_quote {
175-
if ch == '"' {
176-
in_double_quote = false;
177-
}
178-
179-
idx += 1;
180-
continue;
181-
}
182-
183-
match ch {
184-
'\'' => in_single_quote = true,
185-
'"' => in_double_quote = true,
186-
'(' => depth += 1,
187-
')' => {
188-
depth -= 1;
189-
if depth == 0 && idx != bytes.len() - 1 {
190-
return None;
191-
}
192-
}
193-
_ => {}
194-
}
195-
196-
idx += 1;
124+
while value.starts_with('(') && value.ends_with(')') && value.len() > 1 {
125+
value = value[1..value.len() - 1].trim();
197126
}
198127

199-
if depth != 0 || in_single_quote || in_double_quote {
200-
return None;
128+
if value.starts_with('\'') && value.ends_with('\'') && value.len() > 1 {
129+
value = &value[1..value.len() - 1];
201130
}
202131

203-
Some(&value[1..value.len() - 1])
204-
}
205-
206-
fn strip_trailing_top_level_casts(value: &str) -> Option<&str> {
207-
let bytes = value.as_bytes();
208-
let mut depth = 0_i32;
209-
let mut in_single_quote = false;
210-
let mut in_double_quote = false;
211-
let mut idx = 0_usize;
212-
213-
while idx + 1 < bytes.len() {
214-
let ch = bytes[idx] as char;
215-
216-
if in_single_quote {
217-
if ch == '\'' {
218-
if idx + 1 < bytes.len() && bytes[idx + 1] as char == '\'' {
219-
idx += 2;
220-
continue;
221-
}
222-
in_single_quote = false;
223-
}
224-
idx += 1;
225-
continue;
226-
}
227-
228-
if in_double_quote {
229-
if ch == '"' {
230-
in_double_quote = false;
231-
}
232-
idx += 1;
233-
continue;
234-
}
235-
236-
match ch {
237-
'\'' => {
238-
in_single_quote = true;
239-
idx += 1;
240-
continue;
241-
}
242-
'"' => {
243-
in_double_quote = true;
244-
idx += 1;
245-
continue;
246-
}
247-
'(' => {
248-
depth += 1;
249-
idx += 1;
250-
continue;
251-
}
252-
')' => {
253-
depth -= 1;
254-
idx += 1;
255-
continue;
256-
}
257-
':' if depth == 0 && bytes[idx + 1] as char == ':' => {
258-
let suffix = &value[idx..];
259-
if suffix
260-
.chars()
261-
.all(|c| c.is_ascii_alphanumeric() || ":_\".()[] ,\t".contains(c))
262-
{
263-
return Some(value[..idx].trim_end());
264-
}
265-
return None;
266-
}
267-
_ => {
268-
idx += 1;
269-
continue;
270-
}
271-
}
272-
}
273-
274-
None
275-
}
276-
277-
fn is_basic_literal(value: &str) -> bool {
278132
let value = value.trim();
279-
280-
if value.eq_ignore_ascii_case("true")
281-
|| value.eq_ignore_ascii_case("false")
282-
|| value.eq_ignore_ascii_case("null")
283-
{
284-
return true;
133+
if value.is_empty() || !contains_only_basic_chars(value) {
134+
return None;
285135
}
286136

287-
is_numeric_literal(value) || is_single_quoted_literal(value)
137+
Some(value.to_string())
288138
}
289139

290-
fn is_single_quoted_literal(value: &str) -> bool {
291-
let bytes = value.as_bytes();
292-
293-
if bytes.len() < 2 || bytes.first() != Some(&b'\'') || bytes.last() != Some(&b'\'') {
294-
return false;
295-
}
296-
297-
let mut idx = 1_usize;
298-
let end = bytes.len() - 1;
299-
300-
while idx < end {
301-
if bytes[idx] == b'\'' {
302-
if idx + 1 < end && bytes[idx + 1] == b'\'' {
303-
idx += 2;
304-
} else {
305-
return false;
306-
}
307-
} else {
308-
idx += 1;
309-
}
310-
}
311-
312-
true
140+
fn contains_only_basic_chars(value: &str) -> bool {
141+
value
142+
.chars()
143+
.all(|c| c.is_ascii_alphanumeric() || matches!(c, ' ' | '_' | '-' | '.'))
313144
}
314145

315-
fn is_numeric_literal(value: &str) -> bool {
316-
let bytes = value.as_bytes();
317-
if bytes.is_empty() {
318-
return false;
319-
}
320-
321-
let mut idx = 0_usize;
322-
323-
if matches!(bytes[idx], b'+' | b'-') {
324-
idx += 1;
325-
if idx >= bytes.len() {
326-
return false;
327-
}
328-
}
329-
330-
let integer_start = idx;
331-
while idx < bytes.len() && bytes[idx].is_ascii_digit() {
332-
idx += 1;
333-
}
334-
let has_integer_digits = idx > integer_start;
335-
336-
if idx < bytes.len() && bytes[idx] == b'.' {
337-
idx += 1;
338-
let fractional_start = idx;
339-
while idx < bytes.len() && bytes[idx].is_ascii_digit() {
340-
idx += 1;
341-
}
342-
if !has_integer_digits && idx == fractional_start {
343-
return false;
344-
}
345-
} else if !has_integer_digits {
346-
return false;
347-
}
348-
349-
if idx < bytes.len() && matches!(bytes[idx], b'e' | b'E') {
350-
idx += 1;
351-
if idx < bytes.len() && matches!(bytes[idx], b'+' | b'-') {
352-
idx += 1;
353-
}
354-
355-
let exponent_start = idx;
356-
while idx < bytes.len() && bytes[idx].is_ascii_digit() {
357-
idx += 1;
358-
}
359-
360-
if idx == exponent_start {
361-
return false;
362-
}
363-
}
364-
365-
idx == bytes.len()
146+
fn is_type_cast_fragment(value: &str) -> bool {
147+
value.trim().chars().all(|c| {
148+
c.is_ascii_alphanumeric() || matches!(c, ' ' | '_' | '.' | '"' | '[' | ']' | '(' | ')' | ',')
149+
})
366150
}
367151

368152
impl ContextualPriority for Table {
@@ -402,7 +186,7 @@ mod tests {
402186
fn extracts_basic_defaults_with_optional_casts() {
403187
assert_eq!(
404188
extract_basic_default_literal("'anonymous'::text"),
405-
Some("'anonymous'".to_string())
189+
Some("anonymous".to_string())
406190
);
407191
assert_eq!(extract_basic_default_literal("(42)::int8"), Some("42".to_string()));
408192
assert_eq!(
@@ -426,5 +210,9 @@ mod tests {
426210
extract_basic_default_literal("'a'::text || 'b'::text"),
427211
None
428212
);
213+
assert_eq!(
214+
extract_basic_default_literal("'with@symbol'::text"),
215+
None
216+
);
429217
}
430218
}

crates/pgls_hover/tests/snapshots/table_hover_nullable_and_basic_defaults.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ select * from users
1313
```plain
1414
Columns:
1515
- id: int4 - not null
16-
- name: text - nullable - default: 'anonymous'
16+
- name: text - nullable - default: anonymous
1717
- score: int4 - nullable - default: 0
1818
- enabled: bool - nullable - default: false
1919
- created_at: timestamptz - nullable

0 commit comments

Comments
 (0)