From 044c6959b4858345f0a8a839cadfc4bf212b7447 Mon Sep 17 00:00:00 2001 From: Adam Obuchowicz Date: Thu, 6 Mar 2025 13:21:20 +0100 Subject: [PATCH] Review --- .../components/ComponentBrowser.vue | 12 +------ .../components/ComponentBrowser/input.ts | 8 ++--- .../util/ast/__tests__/abstract.test.ts | 14 +++++++++ app/rust-ffi/src/lib.rs | 31 +++++++++++++++++-- app/ydoc-shared/src/ast/ffi.ts | 12 +++++-- app/ydoc-shared/src/ast/token.ts | 2 ++ app/ydoc-shared/src/ast/tree.ts | 8 +++-- 7 files changed, 66 insertions(+), 21 deletions(-) diff --git a/app/gui/src/project-view/components/ComponentBrowser.vue b/app/gui/src/project-view/components/ComponentBrowser.vue index 2e96447f0194..3f736ebe6fb1 100644 --- a/app/gui/src/project-view/components/ComponentBrowser.vue +++ b/app/gui/src/project-view/components/ComponentBrowser.vue @@ -281,7 +281,7 @@ function applyComponent(component: Opt = null) { input.switchToCodeEditMode() return Ok() } - if (component.suggestionId) { + if (component.suggestionId != null) { return input.applySuggestion(component.suggestionId) } else { // Component without suggestion database entry, for example "literal" component. @@ -297,16 +297,6 @@ function acceptComponent(component: Opt = null) { else result.error.log('Cannot apply suggestion') } -// function editComponent(component: Opt = null) { -// const result = applyComponentToInput(component) -// if (result.ok) acceptInput() -// else result.error.log('Cannot apply suggestion') -// const suggestionId = component?.suggestionId ?? selectedSuggestionId.value -// if (suggestionId == null) return input.switchToCodeEditMode() -// const result = input.applySuggestion(suggestionId) -// if (!result.ok) result.error.log('Cannot apply suggestion') -// } - function acceptInput() { const appliedReturnType = input.mode.mode === 'codeEditing' ? diff --git a/app/gui/src/project-view/components/ComponentBrowser/input.ts b/app/gui/src/project-view/components/ComponentBrowser/input.ts index 996f8efebd4d..507838ebbb87 100644 --- a/app/gui/src/project-view/components/ComponentBrowser/input.ts +++ b/app/gui/src/project-view/components/ComponentBrowser/input.ts @@ -32,7 +32,7 @@ export type ComponentBrowserMode = | { mode: 'componentBrowsing' filter: Filter - literal?: Ast.TextLiteral | Ast.NumericLiteral + literal?: Ast.TextLiteral | Ast.NumericLiteral | Ast.NegationApp | undefined } | { mode: 'codeEditing' @@ -111,10 +111,10 @@ export function useComponentBrowserInput( : {}), } } else { - let literal: Ast.MutableTextLiteral | Ast.NumericLiteral | undefined = + let literal: Ast.MutableTextLiteral | Ast.NumericLiteral | Ast.NegationApp | undefined = Ast.TextLiteral.tryParse(text.value) if (literal == null) { - literal = Ast.NumericLiteral.tryParse(text.value) + literal = Ast.NumericLiteral.tryParseWithSign(text.value) } else { literal.fixBoundaries() } @@ -124,7 +124,7 @@ export function useComponentBrowserInput( pattern: text.value, ...(sourceNodeType.value != null ? { selfArg: sourceNodeType.value } : {}), }, - ...(literal ? { literal } : {}), + literal, } } }) diff --git a/app/gui/src/project-view/util/ast/__tests__/abstract.test.ts b/app/gui/src/project-view/util/ast/__tests__/abstract.test.ts index 165f4d497834..177d805aae11 100644 --- a/app/gui/src/project-view/util/ast/__tests__/abstract.test.ts +++ b/app/gui/src/project-view/util/ast/__tests__/abstract.test.ts @@ -1092,6 +1092,20 @@ test('setRawTextContent promotes single-line uninterpolated text to interpolated expect(literal.code()).toBe(`'${escapeTextLiteral(rawText)}'`) }) +test.each` + text | fixed + ${"'abc'"} | ${"'abc'"} + ${'"abc"'} | ${'"abc"'} + ${"'''abc\n cde"} | ${"'''abc\n cde"} + ${"'abc"} | ${"'abc'"} + ${'"abc'} | ${'"abc"'} +`('Fixing boundaries in $text', ({ text, fixed }) => { + const literal = Ast.TextLiteral.tryParse(text) + assert(literal != null) + literal.fixBoundaries() + expect(literal.code()).toBe(fixed) +}) + test.each([ { code: 'operator1', expected: { subject: 'operator1', accesses: [] } }, { code: 'operator1 foo bar', expected: { subject: 'operator1 foo bar', accesses: [] } }, diff --git a/app/rust-ffi/src/lib.rs b/app/rust-ffi/src/lib.rs index 4e8e3c07b798..61f688129ed4 100644 --- a/app/rust-ffi/src/lib.rs +++ b/app/rust-ffi/src/lib.rs @@ -26,14 +26,14 @@ pub fn parse_block(code: &str) -> Vec { enso_parser::format::serialize(&ast).expect("Failed to serialize AST to binary format") } -#[wasm_bindgen] -pub fn is_ident_or_operator(code: &str) -> u32 { +fn starts_with_ident_or_operator(code: &str) -> u32 { let parsed = enso_parser::lexer::run(code); if parsed.internal_error.is_some() { return 0; } let token = match &parsed.value[..] { [token] => token, + [token, ..] if !ONLY_ONE_TOKEN_ALLOWED => token, _ => return 0, }; match &token.variant { @@ -43,6 +43,17 @@ pub fn is_ident_or_operator(code: &str) -> u32 { } } +#[wasm_bindgen] +pub fn is_ident_or_operator(code: &str) -> u32 { + starts_with_ident_or_operator::(code) +} + +#[wasm_bindgen] +pub fn is_first_token_ident_or_operator(code: &str) -> u32 { + starts_with_ident_or_operator::(code) +} + + #[wasm_bindgen] pub fn is_numeric_literal(code: &str) -> bool { let parsed = PARSER.with(|parser| parser.parse_block(code)); @@ -83,4 +94,20 @@ mod tests { assert!(!is_numeric_literal("1234!")); assert!(!is_numeric_literal("1234e5")); } + + #[test] + fn test_checking_ident_or_operator() { + assert_eq!(is_ident_or_operator("abc"), 1); + assert_eq!(is_ident_or_operator("Abc"), 1); + assert_eq!(is_ident_or_operator("abc 14"), 0); + assert_eq!(is_ident_or_operator("+"), 2); + assert_eq!(is_ident_or_operator("+ 2"), 0); + assert_eq!(is_ident_or_operator("[]"), 0); + + assert_eq!(is_first_token_ident_or_operator("abc"), 1); + assert_eq!(is_first_token_ident_or_operator("abc 14"), 1); + assert_eq!(is_first_token_ident_or_operator("+"), 2); + assert_eq!(is_first_token_ident_or_operator("+ 2"), 2); + assert_eq!(is_first_token_ident_or_operator("[]"), 0); + } } diff --git a/app/ydoc-shared/src/ast/ffi.ts b/app/ydoc-shared/src/ast/ffi.ts index b4e07e70c84a..2fd0f79832e1 100644 --- a/app/ydoc-shared/src/ast/ffi.ts +++ b/app/ydoc-shared/src/ast/ffi.ts @@ -6,6 +6,7 @@ import { createXXHash128 } from 'hash-wasm' import type { IDataType } from 'hash-wasm/dist/lib/util' import { + is_first_token_ident_or_operator, is_ident_or_operator, is_numeric_literal, parse_block, @@ -21,5 +22,12 @@ export function xxHash128(input: IDataType) { return xxHasher128.digest() } -/* eslint-disable-next-line camelcase */ -export { is_ident_or_operator, is_numeric_literal, parse_block, parse_doc_to_json, parse_module } +/* eslint-disable camelcase */ +export { + is_first_token_ident_or_operator, + is_ident_or_operator, + is_numeric_literal, + parse_block, + parse_doc_to_json, + parse_module, +} diff --git a/app/ydoc-shared/src/ast/token.ts b/app/ydoc-shared/src/ast/token.ts index 6220b7403e6c..857d3059038c 100644 --- a/app/ydoc-shared/src/ast/token.ts +++ b/app/ydoc-shared/src/ast/token.ts @@ -135,6 +135,8 @@ export function isIdentifier(code: string): code is Identifier { return is_ident_or_operator(code) === 1 } +export function startsWithIdentifier + /** * Whether the given code is a type or constructor identifier. * This is true if the code is an identifier beginning with an uppercase letter. diff --git a/app/ydoc-shared/src/ast/tree.ts b/app/ydoc-shared/src/ast/tree.ts index 7f802edc806c..eaa64a5c25a0 100644 --- a/app/ydoc-shared/src/ast/tree.ts +++ b/app/ydoc-shared/src/ast/tree.ts @@ -1890,13 +1890,17 @@ export class MutableTextLiteral extends TextLiteral implements MutableExpression setBoundaries(code: string) { this.fields.set('open', unspaced(Token.new(code))) - this.fields.set('close', unspaced(Token.new(code))) + if (code.length > 1) { + this.fields.set('close', undefined) + } else { + this.fields.set('close', unspaced(Token.new(code))) + } } fixBoundaries() { const open = this.open const close = this.close - if (open != null && close == null) { + if (open != null && close == null && open.code().length === 1) { this.fields.set('close', unspaced(Token.new(open.code()))) } else if (open == null && close != null) { this.fields.set('open', unspaced(Token.new(close.code())))