diff --git a/packages/language-support/src/formatting/formattingHelpers.ts b/packages/language-support/src/formatting/formattingHelpers.ts index 66adc99ca..f12743513 100644 --- a/packages/language-support/src/formatting/formattingHelpers.ts +++ b/packages/language-support/src/formatting/formattingHelpers.ts @@ -100,6 +100,9 @@ export function isComment(token: Token) { ); } +export const isInlineComment = (chunk: Chunk) => + chunk.comment && chunk.comment.startsWith('/*'); + // Variables or property names that have the same name as a keyword should not be // treated as keywords function isSymbolicName(node: TerminalNode): boolean { @@ -163,20 +166,33 @@ export function fillInRegularChunkGroupSizes( throw new Error(INTERNAL_FORMAT_ERROR_MESSAGE); } group.size += chunk.text.length; + if (isInlineComment(chunk)) { + const inlineComment = ' ' + chunk.comment + ' '; + group.size += inlineComment.length; + group.dbgText += inlineComment; + } + // PERF: Right now we include dbgText always, even though it's only used for debugging. // It does not seem to have any significant performance downsides, but only doing so // when e.g. a flag is set might be a more prudent choice. group.dbgText += chunk.text; - if (!chunk.noSpace && shouldAddSpace(chunk, chunk)) { + if ( + !chunk.noSpace && + shouldAddSpace(chunk, chunk) && + !isInlineComment(chunk) + ) { group.size++; group.dbgText += ' '; } - if (chunk.comment && !groupsEnding.has(group.id)) { + if ( + chunk.comment && + !groupsEnding.has(group.id) && + !isInlineComment(chunk) + ) { group.shouldBreak = true; } } } - export function verifyGroupSizes(chunkList: Chunk[]) { for (const chunk of chunkList) { for (const group of chunk.groupsStarting) { diff --git a/packages/language-support/src/formatting/layoutEngine.ts b/packages/language-support/src/formatting/layoutEngine.ts index 1d5fe967f..9ad02943e 100644 --- a/packages/language-support/src/formatting/layoutEngine.ts +++ b/packages/language-support/src/formatting/layoutEngine.ts @@ -3,6 +3,7 @@ import { Group, IndentationModifier, INTERNAL_FORMAT_ERROR_MESSAGE, + isInlineComment, shouldAddSpace, } from './formattingHelpers'; @@ -122,7 +123,20 @@ function appendChunkText(state: State, chunk: Chunk) { } function handleComments(state: State, chunk: Chunk) { + if (isInlineComment(chunk)) { + // Inline comment - append directly + state.formatted += ' '; + state.formatted += chunk.comment; + state.column += chunk.comment.length; + // Always include space after, even if the chunk has noSpace + if (chunk.type === 'REGULAR' && chunk.noSpace) { + state.formatted += ' '; + state.column++; + } + return; + } if (chunk.comment) { + // For regular comments, we store them to append later state.pendingComments.push(chunk.comment); } } diff --git a/packages/language-support/src/tests/formatting/comments.test.ts b/packages/language-support/src/tests/formatting/comments.test.ts index ff95d956f..4e2c7dfb9 100644 --- a/packages/language-support/src/tests/formatting/comments.test.ts +++ b/packages/language-support/src/tests/formatting/comments.test.ts @@ -48,19 +48,11 @@ RETURN n`; }); test('weird inline comments', () => { - const inlinemultiline = `MERGE (n) /* Ensuring the node exists */ + const inlinemultiline = `MERGE /* Ensuring the node exists */ (n) ON CREATE SET n.prop = 0 /* Set default property */ -MERGE (a:A) /* Create or match 'a:A' */ - -[:T]-> (b:B) /* Link 'a' to 'b' */ -RETURN a.prop /* Return the property of 'a' */ -`; - const expected = `MERGE (n) /* Ensuring the node exists */ - ON CREATE SET n.prop = 0 /* Set default property */ -MERGE - (a:A)- /* Create or match 'a:A' */ - [:T]-> - (b:B) /* Link 'a' to 'b' */ +MERGE /* Create or match 'a:A' */ (a:A)-[:T]->(b:B) // test RETURN a.prop /* Return the property of 'a' */`; + const expected = inlinemultiline; verifyFormatting(inlinemultiline, expected); }); @@ -302,12 +294,8 @@ RETURN n`; const query = ` MATCH (a:Node) // first match WITH a, /* intermediate comment */ a.property AS prop -RETURN prop; // final return`; - const expected = `MATCH (a:Node) // first match -WITH - a, /* intermediate comment */ - a.property AS prop -RETURN prop; // final return`; +RETURN prop; // final return`.trimStart(); + const expected = query; verifyFormatting(query, expected); }); @@ -569,6 +557,43 @@ RETURN m, n`.trim(); const expected = query; verifyFormatting(query, expected); }); + + test('inline comment', () => { + const query = ` +MATCH /* One comment. */ (m)-[:RELATION]->(n) +RETURN m, n`.trim(); + const expected = query; + verifyFormatting(query, expected); + }); + + test('inline comment within a chunk', () => { + const query = `MATCH (m/*comment*/)-[:RELATION]->(n) +RETURN m, n`; + const expected = `MATCH (m)- /*comment*/ [:RELATION]->(n) +RETURN m, n`; + verifyFormatting(query, expected); + }); + + test('long inline comment within a chunk', () => { + const query = `MATCH (m/*commentcommentcommentcommentcommentcommentcommentcom*/)-[:RELATION]->(n) +RETURN m, n`; + const expected = `MATCH + (m)- /*commentcommentcommentcommentcommentcommentcommentcom*/ [:RELATION]->(n) +RETURN m, n`; + verifyFormatting(query, expected); + }); + + test('long inline comment within a chunk should break everything', () => { + // See the extra m at the end of the comment compared to above test, that m makes it over 80 characters + const query = `MATCH (m/*commentcommentcommentcommentcommentcommentcommentcomm*/)-[:RELATION]->(n) +RETURN m, n`; + const expected = `MATCH + (m)- /*commentcommentcommentcommentcommentcommentcommentcomm*/ [:RELATION]-> + (n) +RETURN m, n`; + verifyFormatting(query, expected); + }); + test('long list in a return with comment', () => { const query = ` RETURN // test