Skip to content

Commit f4cc0ca

Browse files
committed
types and add cte comment handling
1 parent e7a8352 commit f4cc0ca

13 files changed

Lines changed: 1199 additions & 617 deletions

demo/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { acceptCompletion } from "@codemirror/autocomplete";
2-
import { keywordCompletionSource, PostgreSQL, sql } from "@codemirror/lang-sql";
2+
import { PostgreSQL, sql } from "@codemirror/lang-sql";
33
import type { EditorState } from "@codemirror/state";
44
import { keymap } from "@codemirror/view";
55
import { basicSetup, EditorView } from "codemirror";

src/sql/__tests__/diagnostics.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Text } from "@codemirror/state";
22
import type { EditorView } from "@codemirror/view";
33
import { describe, expect, it, vi } from "vitest";
44
import { sqlLinter } from "../diagnostics.js";
5-
import type { SqlParser } from "../parser.js";
5+
import type { SqlParser } from "../types.js";
66

77
// Mock EditorView
88
const _createMockView = (content: string) => {

src/sql/__tests__/gutter-diagnostics-integration.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { EditorState, Text } from "@codemirror/state";
2-
import { EditorView } from "@codemirror/view";
32
import { beforeEach, describe, expect, it } from "vitest";
43
import { sqlLinter } from "../diagnostics.js";
54
import { NodeSqlParser } from "../parser.js";

src/sql/__tests__/parser/error-positioning.test.ts

Lines changed: 459 additions & 0 deletions
Large diffs are not rendered by default.

src/sql/diagnostics.ts

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,67 @@ export interface SqlLinterConfig {
1919
/**
2020
* Converts a SQL parse error to a CodeMirror diagnostic
2121
*/
22-
function convertToCodeMirrorDiagnostic(error: SqlParseError, doc: Text): Diagnostic {
23-
const lineStart = doc.line(error.line).from;
22+
export function convertToCodeMirrorDiagnostic(error: SqlParseError, doc: Text): Diagnostic {
23+
const line = doc.line(error.line);
24+
const lineStart = line.from;
25+
26+
// Calculate the start position of the error
2427
const from = lineStart + Math.max(0, error.column - 1);
25-
const to = from + 1;
28+
29+
// For column errors, try to span the entire column name
30+
let to = from + 1; // Default to single character
31+
32+
// If this is a column error, try to find the column name length
33+
if (error.message.includes("Column") && error.message.includes("does not exist")) {
34+
// Extract column name from error message
35+
const columnMatch = error.message.match(/Column '([^']+)' does not exist/);
36+
if (columnMatch?.[1]) {
37+
const columnName = columnMatch[1];
38+
// Find the column name in the line text starting from the error column
39+
const lineText = line.text;
40+
const startSearchPos = Math.max(0, error.column - 1);
41+
const columnIndex = lineText.indexOf(columnName, startSearchPos);
42+
if (columnIndex !== -1) {
43+
to = lineStart + columnIndex + columnName.length;
44+
} else {
45+
// If not found from the error column, try to find it anywhere in the line
46+
const globalIndex = lineText.indexOf(columnName);
47+
if (globalIndex !== -1) {
48+
to = lineStart + globalIndex + columnName.length;
49+
}
50+
}
51+
}
52+
}
53+
54+
// Handle the case where the error is reported on a comment line but should be on the SQL line
55+
// This happens when the parser reports the wrong line number due to comments
56+
if (line.text.trim().startsWith("--")) {
57+
// Find the next non-comment line that contains SQL
58+
for (let i = error.line; i < doc.lines; i++) {
59+
const nextLine = doc.line(i + 1);
60+
const nextLineText = nextLine.text.trim();
61+
if (nextLineText && !nextLineText.startsWith("--")) {
62+
// Found a non-comment line, check if it contains the error
63+
if (error.message.includes("Column") && error.message.includes("does not exist")) {
64+
const columnMatch = error.message.match(/Column '([^']+)' does not exist/);
65+
if (columnMatch?.[1]) {
66+
const columnName = columnMatch[1];
67+
const columnIndex = nextLineText.indexOf(columnName);
68+
if (columnIndex !== -1) {
69+
return {
70+
from: nextLine.from + columnIndex,
71+
to: nextLine.from + columnIndex + columnName.length,
72+
severity: error.severity,
73+
message: error.message,
74+
source: "sql-parser",
75+
};
76+
}
77+
}
78+
}
79+
break;
80+
}
81+
}
82+
}
2683

2784
return {
2885
from,

src/sql/hover.ts

Lines changed: 2 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import {
77
type ResolvedNamespaceItem,
88
resolveNamespaceItem,
99
} from "./namespace-utils.js";
10-
import { NodeSqlParser, type QueryContext } from "./parser.js";
10+
import type { QueryContext } from "./parser/types.js";
11+
import { NodeSqlParser } from "./parser.js";
1112

1213
/**
1314
* SQL schema information for hover tooltips
@@ -87,8 +88,6 @@ export interface SqlHoverConfig {
8788
table?: (data: NamespaceTooltipData) => string;
8889
/** Custom renderer for column items */
8990
column?: (data: NamespaceTooltipData) => string;
90-
/** Custom renderer for non-existent columns */
91-
nonExistentColumn?: (data: NonExistentColumnData) => string;
9291
};
9392
}
9493

@@ -308,27 +307,6 @@ function createColumnTooltipWithContext(params: {
308307
console.log("Hover debug - primaryTable:", primaryTable);
309308
console.log("Hover debug - resolvedSchema:", resolvedSchema);
310309

311-
// Create a parser instance to use its schema validation methods
312-
const parser = new NodeSqlParser();
313-
314-
// Check if column exists in current table
315-
const columnExistsInCurrentTable =
316-
primaryTable && parser.columnExists(resolvedSchema, primaryTable, word);
317-
318-
console.log("Hover debug - columnExistsInCurrentTable:", columnExistsInCurrentTable);
319-
320-
if (!columnExistsInCurrentTable) {
321-
// Column doesn't exist in current table - show error
322-
const nonExistentData: NonExistentColumnData = {
323-
columnName: word,
324-
currentTable: primaryTable,
325-
};
326-
327-
return tooltipRenderers?.nonExistentColumn
328-
? tooltipRenderers.nonExistentColumn(nonExistentData)
329-
: createNonExistentColumnTooltip(nonExistentData);
330-
}
331-
332310
// Column exists in current table - use normal column renderer
333311
return tooltipRenderers?.column
334312
? tooltipRenderers.column(namespaceData)
@@ -360,25 +338,6 @@ function createGenericNamespaceTooltip(params: {
360338
return createNamespaceTooltip(namespaceData.item);
361339
}
362340

363-
/**
364-
* Creates HTML content for non-existent columns
365-
*/
366-
function createNonExistentColumnTooltip(data: NonExistentColumnData): string {
367-
const { columnName, currentTable } = data;
368-
369-
let html = `<div class="sql-hover-column-error">`;
370-
html += `<div class="sql-hover-header"><strong>${columnName}</strong> <span class="sql-hover-type">column</span></div>`;
371-
html += `<div class="sql-hover-description">❌ Column not found`;
372-
if (currentTable) {
373-
html += ` in table <code>${currentTable}</code>`;
374-
}
375-
html += `</div>`;
376-
html += `<div class="sql-hover-suggestion">Check column name spelling or verify table schema</div>`;
377-
html += `</div>`;
378-
379-
return html;
380-
}
381-
382341
/**
383342
* Creates HTML content for namespace-resolved items
384343
*/

0 commit comments

Comments
 (0)