@514labs/moose-lsp 0.1.0-dev.11.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/clickhouseData.d.ts +78 -0
- package/dist/clickhouseData.js +111 -0
- package/dist/clickhouseData.js.map +1 -0
- package/dist/clickhouseData.test.d.ts +2 -0
- package/dist/clickhouseData.test.js +92 -0
- package/dist/clickhouseData.test.js.map +1 -0
- package/dist/clickhouseVersion.d.ts +49 -0
- package/dist/clickhouseVersion.js +148 -0
- package/dist/clickhouseVersion.js.map +1 -0
- package/dist/clickhouseVersion.test.d.ts +2 -0
- package/dist/clickhouseVersion.test.js +113 -0
- package/dist/clickhouseVersion.test.js.map +1 -0
- package/dist/codeActions.d.ts +27 -0
- package/dist/codeActions.js +172 -0
- package/dist/codeActions.js.map +1 -0
- package/dist/codeActions.test.d.ts +2 -0
- package/dist/codeActions.test.js +206 -0
- package/dist/codeActions.test.js.map +1 -0
- package/dist/completions.d.ts +22 -0
- package/dist/completions.js +227 -0
- package/dist/completions.js.map +1 -0
- package/dist/completions.test.d.ts +2 -0
- package/dist/completions.test.js +282 -0
- package/dist/completions.test.js.map +1 -0
- package/dist/data/clickhouse-25.6.json +30772 -0
- package/dist/data/clickhouse-25.8.json +31872 -0
- package/dist/diagnostics.d.ts +18 -0
- package/dist/diagnostics.js +53 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/diagnostics.test.d.ts +2 -0
- package/dist/diagnostics.test.js +109 -0
- package/dist/diagnostics.test.js.map +1 -0
- package/dist/formatting.d.ts +36 -0
- package/dist/formatting.js +69 -0
- package/dist/formatting.js.map +1 -0
- package/dist/formatting.test.d.ts +2 -0
- package/dist/formatting.test.js +92 -0
- package/dist/formatting.test.js.map +1 -0
- package/dist/hover.d.ts +44 -0
- package/dist/hover.js +209 -0
- package/dist/hover.js.map +1 -0
- package/dist/hover.test.d.ts +2 -0
- package/dist/hover.test.js +354 -0
- package/dist/hover.test.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/node_modules/@514labs/moose-sql-validator-wasm/dist/index.js +56 -0
- package/dist/node_modules/@514labs/moose-sql-validator-wasm/dist/index.js.map +1 -0
- package/dist/node_modules/@514labs/moose-sql-validator-wasm/dist/index.test.js +185 -0
- package/dist/node_modules/@514labs/moose-sql-validator-wasm/dist/index.test.js.map +1 -0
- package/dist/node_modules/@514labs/moose-sql-validator-wasm/package.json +40 -0
- package/dist/node_modules/@514labs/moose-sql-validator-wasm/pkg/package.json +11 -0
- package/dist/node_modules/@514labs/moose-sql-validator-wasm/pkg/sql_validator.js +176 -0
- package/dist/node_modules/@514labs/moose-sql-validator-wasm/pkg/sql_validator_bg.wasm +0 -0
- package/dist/projectDetector.d.ts +21 -0
- package/dist/projectDetector.js +162 -0
- package/dist/projectDetector.js.map +1 -0
- package/dist/projectDetector.test.d.ts +2 -0
- package/dist/projectDetector.test.js +303 -0
- package/dist/projectDetector.test.js.map +1 -0
- package/dist/pythonService.d.ts +40 -0
- package/dist/pythonService.js +121 -0
- package/dist/pythonService.js.map +1 -0
- package/dist/pythonService.test.d.ts +2 -0
- package/dist/pythonService.test.js +208 -0
- package/dist/pythonService.test.js.map +1 -0
- package/dist/pythonSqlExtractor.d.ts +25 -0
- package/dist/pythonSqlExtractor.js +258 -0
- package/dist/pythonSqlExtractor.js.map +1 -0
- package/dist/pythonSqlExtractor.test.d.ts +2 -0
- package/dist/pythonSqlExtractor.test.js +227 -0
- package/dist/pythonSqlExtractor.test.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.integration.test.d.ts +2 -0
- package/dist/server.integration.test.js +189 -0
- package/dist/server.integration.test.js.map +1 -0
- package/dist/server.js +228412 -0
- package/dist/server.js.map +1 -0
- package/dist/serverLogic.d.ts +40 -0
- package/dist/serverLogic.js +61 -0
- package/dist/serverLogic.js.map +1 -0
- package/dist/serverLogic.test.d.ts +2 -0
- package/dist/serverLogic.test.js +263 -0
- package/dist/serverLogic.test.js.map +1 -0
- package/dist/sqlExtractor.d.ts +15 -0
- package/dist/sqlExtractor.js +105 -0
- package/dist/sqlExtractor.js.map +1 -0
- package/dist/sqlExtractor.test.d.ts +2 -0
- package/dist/sqlExtractor.test.js +267 -0
- package/dist/sqlExtractor.test.js.map +1 -0
- package/dist/sqlLocations.d.ts +31 -0
- package/dist/sqlLocations.js +51 -0
- package/dist/sqlLocations.js.map +1 -0
- package/dist/sqlLocations.test.d.ts +2 -0
- package/dist/sqlLocations.test.js +94 -0
- package/dist/sqlLocations.test.js.map +1 -0
- package/dist/typescriptService.d.ts +29 -0
- package/dist/typescriptService.js +137 -0
- package/dist/typescriptService.js.map +1 -0
- package/dist/typescriptService.test.d.ts +2 -0
- package/dist/typescriptService.test.js +200 -0
- package/dist/typescriptService.test.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/pythonSqlExtractor.ts"],"sourcesContent":["import Parser from 'tree-sitter';\nimport Python from 'tree-sitter-python';\nimport type { SqlLocation } from './sqlLocations';\n\n// Initialize tree-sitter parser with Python grammar\nconst parser = new Parser();\n// Cast to any to work around type compatibility issues between tree-sitter and tree-sitter-python\nparser.setLanguage(Python as unknown as Parser.Language);\n\n/**\n * Check if the file imports from moose_lib.\n * This is used to filter out sql() calls that aren't from moose-lib.\n */\nfunction fileImportsMooseLib(rootNode: Parser.SyntaxNode): boolean {\n for (const child of rootNode.children) {\n // Handle: import moose_lib\n if (child.type === 'import_statement') {\n const moduleName = child.childForFieldName('name');\n if (moduleName?.text === 'moose_lib') {\n return true;\n }\n // Handle: import moose_lib.sql\n if (\n moduleName?.type === 'dotted_name' &&\n moduleName.text.startsWith('moose_lib')\n ) {\n return true;\n }\n }\n\n // Handle: from moose_lib import sql\n // Handle: from moose_lib.sql import sql\n if (child.type === 'import_from_statement') {\n const moduleName = child.childForFieldName('module_name');\n if (\n moduleName?.text === 'moose_lib' ||\n moduleName?.text?.startsWith('moose_lib.')\n ) {\n return true;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Check if a function call is a `sql()` call from moose-lib.\n * We look for calls where the function name is 'sql'.\n */\nfunction isSqlFunctionCall(node: Parser.SyntaxNode): boolean {\n if (node.type !== 'call') return false;\n\n const functionNode = node.childForFieldName('function');\n if (!functionNode) return false;\n\n // Direct call: sql(\"...\")\n if (functionNode.type === 'identifier' && functionNode.text === 'sql') {\n return true;\n }\n\n // Attribute access: moose_lib.sql(\"...\") or similar\n if (functionNode.type === 'attribute') {\n const attribute = functionNode.childForFieldName('attribute');\n if (attribute?.text === 'sql') {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Extract the SQL string from a sql() function call arguments.\n * Handles both regular strings and f-strings.\n */\nfunction extractSqlFromCall(callNode: Parser.SyntaxNode): {\n text: string;\n startPosition: Parser.Point;\n endPosition: Parser.Point;\n} | null {\n const arguments_ = callNode.childForFieldName('arguments');\n if (!arguments_) return null;\n\n // Get the first argument (the SQL string)\n for (const child of arguments_.children) {\n if (\n child.type === 'string' ||\n child.type === 'concatenated_string' ||\n child.type === 'formatted_string'\n ) {\n const result = extractStringContent(child);\n if (result) {\n return {\n text: result,\n startPosition: child.startPosition,\n endPosition: child.endPosition,\n };\n }\n }\n }\n\n return null;\n}\n\n/**\n * Extract text content from a string node (regular string or f-string).\n * Converts f-string interpolations to ${...} placeholders.\n */\nfunction extractStringContent(node: Parser.SyntaxNode): string | null {\n const text = node.text;\n const isFString = /^f['\"]/.test(text);\n\n if (node.type === 'string') {\n if (isFString) {\n // F-string: f\"SELECT {col} FROM users\"\n return extractFStringContent(text);\n }\n // Regular string: \"SELECT * FROM users\"\n // Remove quotes and extract content\n return extractQuotedStringContent(text);\n }\n\n if (node.type === 'formatted_string') {\n // F-string: f\"SELECT {col} FROM users\"\n return extractFStringContent(text);\n }\n\n if (node.type === 'concatenated_string') {\n // Handle concatenated strings: \"SELECT \" \"* FROM users\"\n let result = '';\n for (const child of node.children) {\n if (child.type === 'string' || child.type === 'formatted_string') {\n const content = extractStringContent(child);\n if (content !== null) {\n result += content;\n }\n }\n }\n return result || null;\n }\n\n return null;\n}\n\n/**\n * Extract content from a quoted string, removing the quotes.\n */\nfunction extractQuotedStringContent(text: string): string | null {\n // Handle triple-quoted strings\n if (\n text.startsWith('\"\"\"') ||\n text.startsWith(\"'''\") ||\n text.startsWith('f\"\"\"') ||\n text.startsWith(\"f'''\") ||\n text.startsWith('r\"\"\"') ||\n text.startsWith(\"r'''\")\n ) {\n const prefixLen = text.startsWith('f') || text.startsWith('r') ? 4 : 3;\n return text.slice(prefixLen, -3);\n }\n\n // Handle single/double quoted strings\n if (\n text.startsWith('\"') ||\n text.startsWith(\"'\") ||\n text.startsWith('f\"') ||\n text.startsWith(\"f'\") ||\n text.startsWith('r\"') ||\n text.startsWith(\"r'\")\n ) {\n const prefixLen = text.startsWith('f') || text.startsWith('r') ? 2 : 1;\n return text.slice(prefixLen, -1);\n }\n\n return text;\n}\n\n/**\n * Extract content from an f-string text, converting interpolations to ${...}.\n * Works with raw text (e.g., f\"SELECT {col} FROM users\").\n */\nfunction extractFStringContent(text: string): string {\n // Remove the f-string prefix (f\", f', f\"\"\", f''')\n let content: string;\n if (text.startsWith('f\"\"\"') || text.startsWith(\"f'''\")) {\n content = text.slice(4, -3);\n } else if (text.startsWith('f\"') || text.startsWith(\"f'\")) {\n content = text.slice(2, -1);\n } else {\n content = text;\n }\n\n // Replace balanced {expr} with ${...} using depth tracking\n // This handles nested braces like {func({1, 2})} correctly\n let result = '';\n let i = 0;\n while (i < content.length) {\n if (content[i] === '{') {\n if (content[i + 1] === '{') {\n // Escaped {{ → literal {\n result += '{';\n i += 2;\n } else {\n // Find matching closing brace with depth tracking\n let depth = 1;\n let j = i + 1;\n while (j < content.length && depth > 0) {\n if (content[j] === '{') depth++;\n else if (content[j] === '}') depth--;\n j++;\n }\n result += '${...}';\n i = j;\n }\n } else if (content[i] === '}' && content[i + 1] === '}') {\n // Escaped }} → literal }\n result += '}';\n i += 2;\n } else {\n result += content[i];\n i++;\n }\n }\n return result;\n}\n\n/**\n * Check if a string node contains SQL-like content.\n * Used to identify f-strings that might contain SQL even outside sql() calls.\n */\nfunction looksLikeSql(text: string): boolean {\n const sqlKeywords = [\n 'SELECT',\n 'INSERT',\n 'UPDATE',\n 'DELETE',\n 'CREATE',\n 'DROP',\n 'ALTER',\n 'FROM',\n 'WHERE',\n 'JOIN',\n 'TABLE',\n 'INDEX',\n 'VIEW',\n ];\n\n const upperText = text.toUpperCase();\n return sqlKeywords.some((keyword) => upperText.includes(keyword));\n}\n\n/**\n * Check if a formatted string contains :col format specifier.\n * This is a moose-lib specific pattern for SQL columns.\n */\nfunction hasColFormatSpecifier(node: Parser.SyntaxNode): boolean {\n const text = node.text;\n // Look for patterns like {var:col} or {Model.field:col}\n return /:col\\s*\\}/.test(text);\n}\n\n/**\n * Extract SQL location from a node.\n */\nfunction createSqlLocation(\n filePath: string,\n text: string,\n startPosition: Parser.Point,\n endPosition: Parser.Point,\n): SqlLocation {\n return {\n id: `${filePath}:${startPosition.row + 1}:${startPosition.column + 1}`,\n file: filePath,\n line: startPosition.row + 1, // 1-based\n column: startPosition.column + 1, // 1-based\n endLine: endPosition.row + 1,\n endColumn: endPosition.column + 1,\n templateText: text,\n };\n}\n\n/**\n * Extract all SQL locations from a Python source file.\n * Finds:\n * 1. sql() function calls with string arguments\n * 2. F-strings with :col format specifiers (moose-lib pattern)\n */\nexport function extractPythonSqlLocations(\n sourceCode: string,\n filePath: string,\n): SqlLocation[] {\n const locations: SqlLocation[] = [];\n\n const tree = parser.parse(sourceCode);\n\n // Only process sql() calls if the file imports from moose_lib\n const hasMooseImport = fileImportsMooseLib(tree.rootNode);\n\n function visit(node: Parser.SyntaxNode): void {\n // Check for sql() function calls (only if file imports moose_lib)\n if (hasMooseImport && isSqlFunctionCall(node)) {\n const sqlContent = extractSqlFromCall(node);\n if (sqlContent) {\n locations.push(\n createSqlLocation(\n filePath,\n sqlContent.text,\n sqlContent.startPosition,\n sqlContent.endPosition,\n ),\n );\n }\n }\n\n // Check for f-strings with :col format specifier (moose-lib SQL pattern)\n // tree-sitter-python may use 'string' or 'formatted_string' for f-strings\n const isFString =\n node.type === 'formatted_string' ||\n (node.type === 'string' && /^f['\"]/.test(node.text));\n\n if (isFString && hasColFormatSpecifier(node)) {\n const content = extractFStringContent(node.text);\n if (content && looksLikeSql(content)) {\n locations.push(\n createSqlLocation(\n filePath,\n content,\n node.startPosition,\n node.endPosition,\n ),\n );\n }\n }\n\n // Recursively visit children\n for (const child of node.children) {\n visit(child);\n }\n }\n\n visit(tree.rootNode);\n return locations;\n}\n\n/**\n * Extract SQL locations from all provided Python files.\n * Used for initial scan of the project.\n */\nexport function extractAllPythonSqlLocations(\n files: Array<{ path: string; content: string }>,\n): SqlLocation[] {\n const allLocations: SqlLocation[] = [];\n\n for (const file of files) {\n const locations = extractPythonSqlLocations(file.content, file.path);\n allLocations.push(...locations);\n }\n\n return allLocations;\n}\n\n/**\n * Get the tree-sitter parser instance.\n * Useful for tests or advanced usage.\n */\nexport function getParser(): Parser {\n return parser;\n}\n"],"mappings":"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","names":["Parser","Python"]}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
var import_node_assert = __toESM(require("node:assert"));
|
|
25
|
+
var import_node_test = require("node:test");
|
|
26
|
+
var import_pythonSqlExtractor = require("./pythonSqlExtractor");
|
|
27
|
+
(0, import_node_test.describe)("pythonSqlExtractor", () => {
|
|
28
|
+
(0, import_node_test.describe)("extractPythonSqlLocations", () => {
|
|
29
|
+
(0, import_node_test.it)("extracts sql() function call with simple string", () => {
|
|
30
|
+
const code = `
|
|
31
|
+
from moose_lib import sql
|
|
32
|
+
|
|
33
|
+
query = sql("SELECT * FROM users")
|
|
34
|
+
`;
|
|
35
|
+
const locations = (0, import_pythonSqlExtractor.extractPythonSqlLocations)(code, "/test/file.py");
|
|
36
|
+
import_node_assert.default.strictEqual(locations.length, 1);
|
|
37
|
+
import_node_assert.default.strictEqual(locations[0].templateText, "SELECT * FROM users");
|
|
38
|
+
import_node_assert.default.strictEqual(locations[0].file, "/test/file.py");
|
|
39
|
+
});
|
|
40
|
+
(0, import_node_test.it)("extracts sql() function call with triple-quoted string", () => {
|
|
41
|
+
const code = `
|
|
42
|
+
from moose_lib import sql
|
|
43
|
+
|
|
44
|
+
query = sql("""
|
|
45
|
+
SELECT *
|
|
46
|
+
FROM users
|
|
47
|
+
WHERE active = true
|
|
48
|
+
""")
|
|
49
|
+
`;
|
|
50
|
+
const locations = (0, import_pythonSqlExtractor.extractPythonSqlLocations)(code, "/test/file.py");
|
|
51
|
+
import_node_assert.default.strictEqual(locations.length, 1);
|
|
52
|
+
import_node_assert.default.ok(locations[0].templateText.includes("SELECT *"));
|
|
53
|
+
import_node_assert.default.ok(locations[0].templateText.includes("FROM users"));
|
|
54
|
+
});
|
|
55
|
+
(0, import_node_test.it)("extracts multiple sql() calls from same file", () => {
|
|
56
|
+
const code = `
|
|
57
|
+
from moose_lib import sql
|
|
58
|
+
|
|
59
|
+
query1 = sql("SELECT * FROM users")
|
|
60
|
+
query2 = sql("SELECT * FROM orders")
|
|
61
|
+
query3 = sql("SELECT * FROM products")
|
|
62
|
+
`;
|
|
63
|
+
const locations = (0, import_pythonSqlExtractor.extractPythonSqlLocations)(code, "/test/file.py");
|
|
64
|
+
import_node_assert.default.strictEqual(locations.length, 3);
|
|
65
|
+
import_node_assert.default.strictEqual(locations[0].templateText, "SELECT * FROM users");
|
|
66
|
+
import_node_assert.default.strictEqual(locations[1].templateText, "SELECT * FROM orders");
|
|
67
|
+
import_node_assert.default.strictEqual(locations[2].templateText, "SELECT * FROM products");
|
|
68
|
+
});
|
|
69
|
+
(0, import_node_test.it)("extracts f-string with :col format specifier", () => {
|
|
70
|
+
const code = `
|
|
71
|
+
from moose_lib import MooseModel
|
|
72
|
+
|
|
73
|
+
class User(MooseModel):
|
|
74
|
+
user_id: int
|
|
75
|
+
email: str
|
|
76
|
+
|
|
77
|
+
query = f"SELECT {User.user_id:col}, {User.email:col} FROM users"
|
|
78
|
+
`;
|
|
79
|
+
const locations = (0, import_pythonSqlExtractor.extractPythonSqlLocations)(code, "/test/file.py");
|
|
80
|
+
import_node_assert.default.strictEqual(locations.length, 1);
|
|
81
|
+
import_node_assert.default.strictEqual(
|
|
82
|
+
locations[0].templateText,
|
|
83
|
+
"SELECT ${...}, ${...} FROM users"
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
(0, import_node_test.it)("extracts sql() with f-string argument", () => {
|
|
87
|
+
const code = `
|
|
88
|
+
from moose_lib import sql
|
|
89
|
+
|
|
90
|
+
table_name = "users"
|
|
91
|
+
query = sql(f"SELECT * FROM {table_name}")
|
|
92
|
+
`;
|
|
93
|
+
const locations = (0, import_pythonSqlExtractor.extractPythonSqlLocations)(code, "/test/file.py");
|
|
94
|
+
import_node_assert.default.strictEqual(locations.length, 1);
|
|
95
|
+
import_node_assert.default.strictEqual(locations[0].templateText, "SELECT * FROM ${...}");
|
|
96
|
+
});
|
|
97
|
+
(0, import_node_test.it)("returns empty array for file without sql patterns", () => {
|
|
98
|
+
const code = `
|
|
99
|
+
def hello():
|
|
100
|
+
print("Hello, World!")
|
|
101
|
+
|
|
102
|
+
x = 1 + 2
|
|
103
|
+
`;
|
|
104
|
+
const locations = (0, import_pythonSqlExtractor.extractPythonSqlLocations)(code, "/test/file.py");
|
|
105
|
+
import_node_assert.default.strictEqual(locations.length, 0);
|
|
106
|
+
});
|
|
107
|
+
(0, import_node_test.it)("ignores f-strings without :col that do not look like SQL", () => {
|
|
108
|
+
const code = `
|
|
109
|
+
name = "Alice"
|
|
110
|
+
greeting = f"Hello, {name}!"
|
|
111
|
+
`;
|
|
112
|
+
const locations = (0, import_pythonSqlExtractor.extractPythonSqlLocations)(code, "/test/file.py");
|
|
113
|
+
import_node_assert.default.strictEqual(locations.length, 0);
|
|
114
|
+
});
|
|
115
|
+
(0, import_node_test.it)("handles attribute access sql calls like moose_lib.sql()", () => {
|
|
116
|
+
const code = `
|
|
117
|
+
import moose_lib
|
|
118
|
+
|
|
119
|
+
query = moose_lib.sql("SELECT * FROM users")
|
|
120
|
+
`;
|
|
121
|
+
const locations = (0, import_pythonSqlExtractor.extractPythonSqlLocations)(code, "/test/file.py");
|
|
122
|
+
import_node_assert.default.strictEqual(locations.length, 1);
|
|
123
|
+
import_node_assert.default.strictEqual(locations[0].templateText, "SELECT * FROM users");
|
|
124
|
+
});
|
|
125
|
+
(0, import_node_test.it)("ignores sql() calls when file does not import moose_lib", () => {
|
|
126
|
+
const code = `
|
|
127
|
+
from sqlalchemy import sql
|
|
128
|
+
|
|
129
|
+
query = sql("SELECT * FROM users")
|
|
130
|
+
`;
|
|
131
|
+
const locations = (0, import_pythonSqlExtractor.extractPythonSqlLocations)(code, "/test/file.py");
|
|
132
|
+
import_node_assert.default.strictEqual(locations.length, 0);
|
|
133
|
+
});
|
|
134
|
+
(0, import_node_test.it)("handles moose_lib submodule imports", () => {
|
|
135
|
+
const code = `
|
|
136
|
+
from moose_lib.sql import sql
|
|
137
|
+
|
|
138
|
+
query = sql("SELECT * FROM users")
|
|
139
|
+
`;
|
|
140
|
+
const locations = (0, import_pythonSqlExtractor.extractPythonSqlLocations)(code, "/test/file.py");
|
|
141
|
+
import_node_assert.default.strictEqual(locations.length, 1);
|
|
142
|
+
import_node_assert.default.strictEqual(locations[0].templateText, "SELECT * FROM users");
|
|
143
|
+
});
|
|
144
|
+
(0, import_node_test.it)("extracts correct line and column positions", () => {
|
|
145
|
+
const code = `from moose_lib import sql
|
|
146
|
+
|
|
147
|
+
query = sql("SELECT * FROM users")
|
|
148
|
+
`;
|
|
149
|
+
const locations = (0, import_pythonSqlExtractor.extractPythonSqlLocations)(code, "/test/file.py");
|
|
150
|
+
import_node_assert.default.strictEqual(locations.length, 1);
|
|
151
|
+
import_node_assert.default.strictEqual(locations[0].line, 3);
|
|
152
|
+
import_node_assert.default.ok(locations[0].column > 0);
|
|
153
|
+
});
|
|
154
|
+
(0, import_node_test.it)("generates unique IDs for each location", () => {
|
|
155
|
+
const code = `
|
|
156
|
+
from moose_lib import sql
|
|
157
|
+
|
|
158
|
+
query1 = sql("SELECT * FROM users")
|
|
159
|
+
query2 = sql("SELECT * FROM orders")
|
|
160
|
+
`;
|
|
161
|
+
const locations = (0, import_pythonSqlExtractor.extractPythonSqlLocations)(code, "/test/file.py");
|
|
162
|
+
import_node_assert.default.strictEqual(locations.length, 2);
|
|
163
|
+
import_node_assert.default.notStrictEqual(locations[0].id, locations[1].id);
|
|
164
|
+
});
|
|
165
|
+
(0, import_node_test.it)("handles f-string with nested braces in expression", () => {
|
|
166
|
+
const code = `
|
|
167
|
+
from moose_lib import sql
|
|
168
|
+
|
|
169
|
+
query = sql(f"SELECT {func({1, 2})} FROM users")
|
|
170
|
+
`;
|
|
171
|
+
const locations = (0, import_pythonSqlExtractor.extractPythonSqlLocations)(code, "/test/file.py");
|
|
172
|
+
import_node_assert.default.strictEqual(locations.length, 1);
|
|
173
|
+
import_node_assert.default.strictEqual(locations[0].templateText, "SELECT ${...} FROM users");
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
(0, import_node_test.describe)("extractAllPythonSqlLocations", () => {
|
|
177
|
+
(0, import_node_test.it)("extracts sql from multiple files", () => {
|
|
178
|
+
const files = [
|
|
179
|
+
{
|
|
180
|
+
path: "/test/queries/users.py",
|
|
181
|
+
content: `
|
|
182
|
+
from moose_lib import sql
|
|
183
|
+
users_query = sql("SELECT * FROM users")
|
|
184
|
+
`
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
path: "/test/queries/orders.py",
|
|
188
|
+
content: `
|
|
189
|
+
from moose_lib import sql
|
|
190
|
+
orders_query = sql("SELECT * FROM orders")
|
|
191
|
+
`
|
|
192
|
+
}
|
|
193
|
+
];
|
|
194
|
+
const locations = (0, import_pythonSqlExtractor.extractAllPythonSqlLocations)(files);
|
|
195
|
+
import_node_assert.default.strictEqual(locations.length, 2);
|
|
196
|
+
const templates = locations.map((l) => l.templateText);
|
|
197
|
+
import_node_assert.default.ok(templates.includes("SELECT * FROM users"));
|
|
198
|
+
import_node_assert.default.ok(templates.includes("SELECT * FROM orders"));
|
|
199
|
+
});
|
|
200
|
+
(0, import_node_test.it)("returns empty array for empty file list", () => {
|
|
201
|
+
const locations = (0, import_pythonSqlExtractor.extractAllPythonSqlLocations)([]);
|
|
202
|
+
import_node_assert.default.strictEqual(locations.length, 0);
|
|
203
|
+
});
|
|
204
|
+
(0, import_node_test.it)("skips files without sql patterns", () => {
|
|
205
|
+
const files = [
|
|
206
|
+
{
|
|
207
|
+
path: "/test/utils.py",
|
|
208
|
+
content: `
|
|
209
|
+
def helper():
|
|
210
|
+
return 42
|
|
211
|
+
`
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
path: "/test/queries.py",
|
|
215
|
+
content: `
|
|
216
|
+
from moose_lib import sql
|
|
217
|
+
query = sql("SELECT * FROM users")
|
|
218
|
+
`
|
|
219
|
+
}
|
|
220
|
+
];
|
|
221
|
+
const locations = (0, import_pythonSqlExtractor.extractAllPythonSqlLocations)(files);
|
|
222
|
+
import_node_assert.default.strictEqual(locations.length, 1);
|
|
223
|
+
import_node_assert.default.strictEqual(locations[0].templateText, "SELECT * FROM users");
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
//# sourceMappingURL=pythonSqlExtractor.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/pythonSqlExtractor.test.ts"],"sourcesContent":["import assert from 'node:assert';\nimport { describe, it } from 'node:test';\nimport {\n extractAllPythonSqlLocations,\n extractPythonSqlLocations,\n} from './pythonSqlExtractor';\n\ndescribe('pythonSqlExtractor', () => {\n describe('extractPythonSqlLocations', () => {\n it('extracts sql() function call with simple string', () => {\n const code = `\nfrom moose_lib import sql\n\nquery = sql(\"SELECT * FROM users\")\n`;\n const locations = extractPythonSqlLocations(code, '/test/file.py');\n\n assert.strictEqual(locations.length, 1);\n assert.strictEqual(locations[0].templateText, 'SELECT * FROM users');\n assert.strictEqual(locations[0].file, '/test/file.py');\n });\n\n it('extracts sql() function call with triple-quoted string', () => {\n const code = `\nfrom moose_lib import sql\n\nquery = sql(\"\"\"\n SELECT *\n FROM users\n WHERE active = true\n\"\"\")\n`;\n const locations = extractPythonSqlLocations(code, '/test/file.py');\n\n assert.strictEqual(locations.length, 1);\n assert.ok(locations[0].templateText.includes('SELECT *'));\n assert.ok(locations[0].templateText.includes('FROM users'));\n });\n\n it('extracts multiple sql() calls from same file', () => {\n const code = `\nfrom moose_lib import sql\n\nquery1 = sql(\"SELECT * FROM users\")\nquery2 = sql(\"SELECT * FROM orders\")\nquery3 = sql(\"SELECT * FROM products\")\n`;\n const locations = extractPythonSqlLocations(code, '/test/file.py');\n\n assert.strictEqual(locations.length, 3);\n assert.strictEqual(locations[0].templateText, 'SELECT * FROM users');\n assert.strictEqual(locations[1].templateText, 'SELECT * FROM orders');\n assert.strictEqual(locations[2].templateText, 'SELECT * FROM products');\n });\n\n it('extracts f-string with :col format specifier', () => {\n const code = `\nfrom moose_lib import MooseModel\n\nclass User(MooseModel):\n user_id: int\n email: str\n\nquery = f\"SELECT {User.user_id:col}, {User.email:col} FROM users\"\n`;\n const locations = extractPythonSqlLocations(code, '/test/file.py');\n\n assert.strictEqual(locations.length, 1);\n // Interpolations should be replaced with ${...}\n assert.strictEqual(\n locations[0].templateText,\n 'SELECT ${...}, ${...} FROM users',\n );\n });\n\n it('extracts sql() with f-string argument', () => {\n const code = `\nfrom moose_lib import sql\n\ntable_name = \"users\"\nquery = sql(f\"SELECT * FROM {table_name}\")\n`;\n const locations = extractPythonSqlLocations(code, '/test/file.py');\n\n assert.strictEqual(locations.length, 1);\n assert.strictEqual(locations[0].templateText, 'SELECT * FROM ${...}');\n });\n\n it('returns empty array for file without sql patterns', () => {\n const code = `\ndef hello():\n print(\"Hello, World!\")\n\nx = 1 + 2\n`;\n const locations = extractPythonSqlLocations(code, '/test/file.py');\n\n assert.strictEqual(locations.length, 0);\n });\n\n it('ignores f-strings without :col that do not look like SQL', () => {\n const code = `\nname = \"Alice\"\ngreeting = f\"Hello, {name}!\"\n`;\n const locations = extractPythonSqlLocations(code, '/test/file.py');\n\n // Should not extract this as it's not SQL\n assert.strictEqual(locations.length, 0);\n });\n\n it('handles attribute access sql calls like moose_lib.sql()', () => {\n const code = `\nimport moose_lib\n\nquery = moose_lib.sql(\"SELECT * FROM users\")\n`;\n const locations = extractPythonSqlLocations(code, '/test/file.py');\n\n assert.strictEqual(locations.length, 1);\n assert.strictEqual(locations[0].templateText, 'SELECT * FROM users');\n });\n\n it('ignores sql() calls when file does not import moose_lib', () => {\n const code = `\nfrom sqlalchemy import sql\n\nquery = sql(\"SELECT * FROM users\")\n`;\n const locations = extractPythonSqlLocations(code, '/test/file.py');\n\n // Should not extract sql() calls from non-moose files\n assert.strictEqual(locations.length, 0);\n });\n\n it('handles moose_lib submodule imports', () => {\n const code = `\nfrom moose_lib.sql import sql\n\nquery = sql(\"SELECT * FROM users\")\n`;\n const locations = extractPythonSqlLocations(code, '/test/file.py');\n\n assert.strictEqual(locations.length, 1);\n assert.strictEqual(locations[0].templateText, 'SELECT * FROM users');\n });\n\n it('extracts correct line and column positions', () => {\n const code = `from moose_lib import sql\n\nquery = sql(\"SELECT * FROM users\")\n`;\n const locations = extractPythonSqlLocations(code, '/test/file.py');\n\n assert.strictEqual(locations.length, 1);\n assert.strictEqual(locations[0].line, 3);\n assert.ok(locations[0].column > 0);\n });\n\n it('generates unique IDs for each location', () => {\n const code = `\nfrom moose_lib import sql\n\nquery1 = sql(\"SELECT * FROM users\")\nquery2 = sql(\"SELECT * FROM orders\")\n`;\n const locations = extractPythonSqlLocations(code, '/test/file.py');\n\n assert.strictEqual(locations.length, 2);\n assert.notStrictEqual(locations[0].id, locations[1].id);\n });\n\n it('handles f-string with nested braces in expression', () => {\n const code = `\nfrom moose_lib import sql\n\nquery = sql(f\"SELECT {func({1, 2})} FROM users\")\n`;\n const locations = extractPythonSqlLocations(code, '/test/file.py');\n\n assert.strictEqual(locations.length, 1);\n assert.strictEqual(locations[0].templateText, 'SELECT ${...} FROM users');\n });\n });\n\n describe('extractAllPythonSqlLocations', () => {\n it('extracts sql from multiple files', () => {\n const files = [\n {\n path: '/test/queries/users.py',\n content: `\nfrom moose_lib import sql\nusers_query = sql(\"SELECT * FROM users\")\n`,\n },\n {\n path: '/test/queries/orders.py',\n content: `\nfrom moose_lib import sql\norders_query = sql(\"SELECT * FROM orders\")\n`,\n },\n ];\n\n const locations = extractAllPythonSqlLocations(files);\n\n assert.strictEqual(locations.length, 2);\n\n const templates = locations.map((l) => l.templateText);\n assert.ok(templates.includes('SELECT * FROM users'));\n assert.ok(templates.includes('SELECT * FROM orders'));\n });\n\n it('returns empty array for empty file list', () => {\n const locations = extractAllPythonSqlLocations([]);\n assert.strictEqual(locations.length, 0);\n });\n\n it('skips files without sql patterns', () => {\n const files = [\n {\n path: '/test/utils.py',\n content: `\ndef helper():\n return 42\n`,\n },\n {\n path: '/test/queries.py',\n content: `\nfrom moose_lib import sql\nquery = sql(\"SELECT * FROM users\")\n`,\n },\n ];\n\n const locations = extractAllPythonSqlLocations(files);\n\n assert.strictEqual(locations.length, 1);\n assert.strictEqual(locations[0].templateText, 'SELECT * FROM users');\n });\n });\n});\n"],"mappings":"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","names":["assert"]}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
var import_node_assert = __toESM(require("node:assert"));
|
|
25
|
+
var import_node_child_process = require("node:child_process");
|
|
26
|
+
var path = __toESM(require("node:path"));
|
|
27
|
+
var import_node_test = require("node:test");
|
|
28
|
+
class LspClient {
|
|
29
|
+
constructor(serverPath2) {
|
|
30
|
+
this.buffer = "";
|
|
31
|
+
this.messages = [];
|
|
32
|
+
this.nextId = 1;
|
|
33
|
+
this.process = (0, import_node_child_process.spawn)("node", [serverPath2, "--stdio"], {
|
|
34
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
35
|
+
});
|
|
36
|
+
this.process.stdout?.on("data", (data) => {
|
|
37
|
+
this.buffer += data.toString();
|
|
38
|
+
this.parseMessages();
|
|
39
|
+
});
|
|
40
|
+
this.process.stderr?.on("data", (_data) => {
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
parseMessages() {
|
|
44
|
+
while (true) {
|
|
45
|
+
const headerEnd = this.buffer.indexOf("\r\n\r\n");
|
|
46
|
+
if (headerEnd === -1) break;
|
|
47
|
+
const header = this.buffer.slice(0, headerEnd);
|
|
48
|
+
const match = header.match(/Content-Length: (\d+)/);
|
|
49
|
+
if (!match) break;
|
|
50
|
+
const contentLength = parseInt(match[1], 10);
|
|
51
|
+
const contentStart = headerEnd + 4;
|
|
52
|
+
const contentEnd = contentStart + contentLength;
|
|
53
|
+
if (this.buffer.length < contentEnd) break;
|
|
54
|
+
const content = this.buffer.slice(contentStart, contentEnd);
|
|
55
|
+
this.buffer = this.buffer.slice(contentEnd);
|
|
56
|
+
const message = JSON.parse(content);
|
|
57
|
+
this.messages.push(message);
|
|
58
|
+
if (this.onMessage) {
|
|
59
|
+
this.onMessage(message);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
send(message) {
|
|
64
|
+
const content = JSON.stringify(message);
|
|
65
|
+
const header = `Content-Length: ${Buffer.byteLength(content)}\r
|
|
66
|
+
\r
|
|
67
|
+
`;
|
|
68
|
+
this.process.stdin?.write(header + content);
|
|
69
|
+
}
|
|
70
|
+
async request(method, params, timeoutMs = 5e3) {
|
|
71
|
+
const id = this.nextId++;
|
|
72
|
+
return new Promise((resolve, reject) => {
|
|
73
|
+
const timeout = setTimeout(() => {
|
|
74
|
+
this.onMessage = void 0;
|
|
75
|
+
reject(new Error(`Timeout waiting for response to ${method}`));
|
|
76
|
+
}, timeoutMs);
|
|
77
|
+
this.onMessage = (msg) => {
|
|
78
|
+
if (msg.id === id) {
|
|
79
|
+
clearTimeout(timeout);
|
|
80
|
+
this.onMessage = void 0;
|
|
81
|
+
resolve(msg);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const existing = this.messages.find((m) => m.id === id);
|
|
85
|
+
if (existing) {
|
|
86
|
+
clearTimeout(timeout);
|
|
87
|
+
this.onMessage = void 0;
|
|
88
|
+
resolve(existing);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
this.send({ jsonrpc: "2.0", id, method, params });
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
notify(method, params) {
|
|
95
|
+
this.send({ jsonrpc: "2.0", method, params });
|
|
96
|
+
}
|
|
97
|
+
async waitForCondition(predicate, timeoutMs = 5e3) {
|
|
98
|
+
const deadline = Date.now() + timeoutMs;
|
|
99
|
+
while (Date.now() < deadline) {
|
|
100
|
+
if (predicate(this.messages)) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
104
|
+
}
|
|
105
|
+
throw new Error("Timeout waiting for condition");
|
|
106
|
+
}
|
|
107
|
+
getMessages() {
|
|
108
|
+
return [...this.messages];
|
|
109
|
+
}
|
|
110
|
+
close() {
|
|
111
|
+
this.process.kill();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const serverPath = path.join(__dirname, "..", "dist", "server.js");
|
|
115
|
+
(0, import_node_test.test)("Server Integration Tests", async (t) => {
|
|
116
|
+
await t.test("responds to initialize request", async () => {
|
|
117
|
+
const client = new LspClient(serverPath);
|
|
118
|
+
try {
|
|
119
|
+
const response = await client.request("initialize", {
|
|
120
|
+
processId: null,
|
|
121
|
+
rootUri: "file:///tmp/test-project",
|
|
122
|
+
capabilities: {}
|
|
123
|
+
});
|
|
124
|
+
import_node_assert.default.ok(response.result, "Should have result");
|
|
125
|
+
const result = response.result;
|
|
126
|
+
import_node_assert.default.ok(result.capabilities, "Should have capabilities");
|
|
127
|
+
import_node_assert.default.ok(
|
|
128
|
+
result.capabilities.textDocumentSync,
|
|
129
|
+
"Should have textDocumentSync capability"
|
|
130
|
+
);
|
|
131
|
+
} finally {
|
|
132
|
+
client.close();
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
await t.test("handles didSave notification and logs message", async () => {
|
|
136
|
+
const client = new LspClient(serverPath);
|
|
137
|
+
try {
|
|
138
|
+
await client.request("initialize", {
|
|
139
|
+
processId: null,
|
|
140
|
+
rootUri: "file:///tmp/test-project",
|
|
141
|
+
capabilities: {
|
|
142
|
+
textDocument: {
|
|
143
|
+
synchronization: {
|
|
144
|
+
didSave: true
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
client.notify("initialized", {});
|
|
150
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
151
|
+
client.notify("textDocument/didOpen", {
|
|
152
|
+
textDocument: {
|
|
153
|
+
uri: "file:///tmp/test-project/app/test.ts",
|
|
154
|
+
languageId: "typescript",
|
|
155
|
+
version: 1,
|
|
156
|
+
text: "const x = 1;"
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
160
|
+
client.notify("textDocument/didSave", {
|
|
161
|
+
textDocument: {
|
|
162
|
+
uri: "file:///tmp/test-project/app/test.ts"
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
await client.waitForCondition((messages2) => {
|
|
166
|
+
return messages2.some((m) => {
|
|
167
|
+
if (m.method !== "window/logMessage") return false;
|
|
168
|
+
const params = m.params;
|
|
169
|
+
return params.message.includes("didSave received");
|
|
170
|
+
});
|
|
171
|
+
}, 3e3);
|
|
172
|
+
const messages = client.getMessages();
|
|
173
|
+
const logMessages = messages.filter(
|
|
174
|
+
(m) => m.method === "window/logMessage"
|
|
175
|
+
);
|
|
176
|
+
const didSaveLog = logMessages.find((m) => {
|
|
177
|
+
const params = m.params;
|
|
178
|
+
return params.message.includes("didSave received");
|
|
179
|
+
});
|
|
180
|
+
import_node_assert.default.ok(
|
|
181
|
+
didSaveLog,
|
|
182
|
+
`Should log didSave received message. Got: ${JSON.stringify(logMessages.map((m) => m.params.message))}`
|
|
183
|
+
);
|
|
184
|
+
} finally {
|
|
185
|
+
client.close();
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
//# sourceMappingURL=server.integration.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server.integration.test.ts"],"sourcesContent":["import assert from 'node:assert';\nimport { type ChildProcess, spawn } from 'node:child_process';\nimport * as path from 'node:path';\nimport { test } from 'node:test';\n\ninterface LspMessage {\n jsonrpc: '2.0';\n id?: number;\n method?: string;\n params?: unknown;\n result?: unknown;\n error?: unknown;\n}\n\nclass LspClient {\n private process: ChildProcess;\n private buffer = '';\n private messages: LspMessage[] = [];\n private nextId = 1;\n private onMessage?: (msg: LspMessage) => void;\n\n constructor(serverPath: string) {\n this.process = spawn('node', [serverPath, '--stdio'], {\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n this.process.stdout?.on('data', (data: Buffer) => {\n this.buffer += data.toString();\n this.parseMessages();\n });\n\n this.process.stderr?.on('data', (_data: Buffer) => {\n // Uncomment for debugging:\n // console.error('LSP stderr:', _data.toString());\n });\n }\n\n private parseMessages() {\n while (true) {\n const headerEnd = this.buffer.indexOf('\\r\\n\\r\\n');\n if (headerEnd === -1) break;\n\n const header = this.buffer.slice(0, headerEnd);\n const match = header.match(/Content-Length: (\\d+)/);\n if (!match) break;\n\n const contentLength = parseInt(match[1], 10);\n const contentStart = headerEnd + 4;\n const contentEnd = contentStart + contentLength;\n\n if (this.buffer.length < contentEnd) break;\n\n const content = this.buffer.slice(contentStart, contentEnd);\n this.buffer = this.buffer.slice(contentEnd);\n\n const message = JSON.parse(content) as LspMessage;\n this.messages.push(message);\n\n if (this.onMessage) {\n this.onMessage(message);\n }\n }\n }\n\n send(message: LspMessage): void {\n const content = JSON.stringify(message);\n const header = `Content-Length: ${Buffer.byteLength(content)}\\r\\n\\r\\n`;\n this.process.stdin?.write(header + content);\n }\n\n async request(\n method: string,\n params: unknown,\n timeoutMs = 5000,\n ): Promise<LspMessage> {\n const id = this.nextId++;\n\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.onMessage = undefined;\n reject(new Error(`Timeout waiting for response to ${method}`));\n }, timeoutMs);\n\n this.onMessage = (msg) => {\n if (msg.id === id) {\n clearTimeout(timeout);\n this.onMessage = undefined;\n resolve(msg);\n }\n };\n\n // Check if response already arrived\n const existing = this.messages.find((m) => m.id === id);\n if (existing) {\n clearTimeout(timeout);\n this.onMessage = undefined;\n resolve(existing);\n return;\n }\n\n this.send({ jsonrpc: '2.0', id, method, params });\n });\n }\n\n notify(method: string, params: unknown): void {\n this.send({ jsonrpc: '2.0', method, params });\n }\n\n async waitForCondition(\n predicate: (messages: LspMessage[]) => boolean,\n timeoutMs = 5000,\n ): Promise<void> {\n const deadline = Date.now() + timeoutMs;\n\n while (Date.now() < deadline) {\n if (predicate(this.messages)) {\n return;\n }\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n\n throw new Error('Timeout waiting for condition');\n }\n\n getMessages(): LspMessage[] {\n return [...this.messages];\n }\n\n close(): void {\n this.process.kill();\n }\n}\n\nconst serverPath = path.join(__dirname, '..', 'dist', 'server.js');\n\ntest('Server Integration Tests', async (t) => {\n await t.test('responds to initialize request', async () => {\n const client = new LspClient(serverPath);\n\n try {\n const response = await client.request('initialize', {\n processId: null,\n rootUri: 'file:///tmp/test-project',\n capabilities: {},\n });\n\n assert.ok(response.result, 'Should have result');\n const result = response.result as {\n capabilities: { textDocumentSync: unknown };\n };\n assert.ok(result.capabilities, 'Should have capabilities');\n assert.ok(\n result.capabilities.textDocumentSync,\n 'Should have textDocumentSync capability',\n );\n } finally {\n client.close();\n }\n });\n\n await t.test('handles didSave notification and logs message', async () => {\n const client = new LspClient(serverPath);\n\n try {\n // Initialize\n await client.request('initialize', {\n processId: null,\n rootUri: 'file:///tmp/test-project',\n capabilities: {\n textDocument: {\n synchronization: {\n didSave: true,\n },\n },\n },\n });\n\n // Send initialized\n client.notify('initialized', {});\n\n // Wait a bit for server to be ready\n await new Promise((resolve) => setTimeout(resolve, 100));\n\n // Must send didOpen before didSave (TextDocuments requires this)\n client.notify('textDocument/didOpen', {\n textDocument: {\n uri: 'file:///tmp/test-project/app/test.ts',\n languageId: 'typescript',\n version: 1,\n text: 'const x = 1;',\n },\n });\n\n await new Promise((resolve) => setTimeout(resolve, 50));\n\n // Send didSave\n client.notify('textDocument/didSave', {\n textDocument: {\n uri: 'file:///tmp/test-project/app/test.ts',\n },\n });\n\n // Wait for the didSave log message\n await client.waitForCondition((messages) => {\n return messages.some((m) => {\n if (m.method !== 'window/logMessage') return false;\n const params = m.params as { message: string };\n return params.message.includes('didSave received');\n });\n }, 3000);\n\n // Verify\n const messages = client.getMessages();\n const logMessages = messages.filter(\n (m) => m.method === 'window/logMessage',\n );\n const didSaveLog = logMessages.find((m) => {\n const params = m.params as { message: string };\n return params.message.includes('didSave received');\n });\n\n assert.ok(\n didSaveLog,\n `Should log didSave received message. Got: ${JSON.stringify(logMessages.map((m) => (m.params as { message: string }).message))}`,\n );\n } finally {\n client.close();\n }\n });\n});\n"],"mappings":"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","names":["serverPath","assert","messages"]}
|