дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 11:15:40 +02:00
chore: Format the codebase with oxfmt and check formatting in CI (#135)
Run oxfmt once over the whole repository (formatting only, no logic changes) and add `pnpm format:check` to the frontend CI job so unformatted code no longer builds up. `.oxfmtrc.json` now skips the generated theme docs, test fixtures and code blocks inside Markdown. Co-authored-by: jaw52 <[email protected]>
Цей коміт міститься в:
380 files changed
+6409
-1634
No files matched your search
@@ -536,7 +536,8 @@ export function valuesSlot(text: string, pos: number, driver: DbDriver): ValuesS
|
||||
continue;
|
||||
}
|
||||
if (depth === 0 && isW(t, "VALUES")) afterValues = true;
|
||||
else if (depth === 1 && !afterValues && columns && (t.kind === "word" || t.kind === "quoted")) columns.push(identName(t));
|
||||
else if (depth === 1 && !afterValues && columns && (t.kind === "word" || t.kind === "quoted"))
|
||||
columns.push(identName(t));
|
||||
else if (depth === 1 && inTuple && isP(t, ",")) index++;
|
||||
}
|
||||
return inTuple ? { target, columns: columns?.length ? columns : null, index } : null;
|
||||
|
||||
@@ -10,7 +10,9 @@ const fmt = (sql: string) => {
|
||||
describe("formatSql", () => {
|
||||
it("lays out a query like IDEA", () => {
|
||||
expect(
|
||||
fmt("SELECT u.id,u.name , count( * ) AS n FROM users u LEFT JOIN orders o ON o.user_id=u.id WHERE u.id>1 AND o.total BETWEEN 1 AND 5 OR o.status='x' GROUP BY u.id ORDER BY n DESC LIMIT 10;"),
|
||||
fmt(
|
||||
"SELECT u.id,u.name , count( * ) AS n FROM users u LEFT JOIN orders o ON o.user_id=u.id WHERE u.id>1 AND o.total BETWEEN 1 AND 5 OR o.status='x' GROUP BY u.id ORDER BY n DESC LIMIT 10;",
|
||||
),
|
||||
).toBe(
|
||||
[
|
||||
"SELECT u.id,",
|
||||
@@ -29,7 +31,11 @@ describe("formatSql", () => {
|
||||
});
|
||||
|
||||
it("aligns subqueries and CTEs after their parenthesis", () => {
|
||||
expect(fmt("with big as (select user_id, total from orders where total > 10) select * from big where exists (select 1 from users u where u.id = big.user_id);")).toBe(
|
||||
expect(
|
||||
fmt(
|
||||
"with big as (select user_id, total from orders where total > 10) select * from big where exists (select 1 from users u where u.id = big.user_id);",
|
||||
),
|
||||
).toBe(
|
||||
[
|
||||
"with big as (select user_id,",
|
||||
" total",
|
||||
|
||||
@@ -25,11 +25,21 @@ interface Item {
|
||||
}
|
||||
|
||||
/** Edits formatting every statement (or those overlapping `range`). */
|
||||
export function formatSql(text: string, driver: DbDriver, range: { from: number; to: number } | null = null): SqlEdit[] {
|
||||
export function formatSql(
|
||||
text: string,
|
||||
driver: DbDriver,
|
||||
range: { from: number; to: number } | null = null,
|
||||
): SqlEdit[] {
|
||||
const lexed = lex(text, driver);
|
||||
const items: Item[] = [
|
||||
...lexed.tokens,
|
||||
...lexed.comments.map((c) => ({ kind: "comment" as const, text: text.slice(c.from, c.to), upper: "", from: c.from, to: c.to })),
|
||||
...lexed.comments.map((c) => ({
|
||||
kind: "comment" as const,
|
||||
text: text.slice(c.from, c.to),
|
||||
upper: "",
|
||||
from: c.from,
|
||||
to: c.to,
|
||||
})),
|
||||
].sort((a, b) => a.from - b.from);
|
||||
|
||||
const edits: SqlEdit[] = [];
|
||||
@@ -60,7 +70,23 @@ export function formatSql(text: string, driver: DbDriver, range: { from: number;
|
||||
return edits;
|
||||
}
|
||||
|
||||
const NEWLINE_CLAUSES = new Set(["SELECT", "FROM", "WHERE", "GROUP", "HAVING", "ORDER", "LIMIT", "OFFSET", "WINDOW", "RETURNING", "VALUES", "SET", "UNION", "INTERSECT", "EXCEPT"]);
|
||||
const NEWLINE_CLAUSES = new Set([
|
||||
"SELECT",
|
||||
"FROM",
|
||||
"WHERE",
|
||||
"GROUP",
|
||||
"HAVING",
|
||||
"ORDER",
|
||||
"LIMIT",
|
||||
"OFFSET",
|
||||
"WINDOW",
|
||||
"RETURNING",
|
||||
"VALUES",
|
||||
"SET",
|
||||
"UNION",
|
||||
"INTERSECT",
|
||||
"EXCEPT",
|
||||
]);
|
||||
const JOIN_START = new Set(["JOIN", "LEFT", "RIGHT", "FULL", "INNER", "CROSS", "NATURAL", "STRAIGHT_JOIN"]);
|
||||
const JOIN_MODIFIERS = new Set(["LEFT", "RIGHT", "FULL", "INNER", "CROSS", "NATURAL", "OUTER"]);
|
||||
/** Where a list item continues: the width of `select `, `from `, `set `, `values `. */
|
||||
@@ -190,11 +216,17 @@ function spaceBefore(prev: Item | null, it: Item, beforePrev: Item | undefined,
|
||||
|
||||
/** `into users (` — a column list, written with a space. */
|
||||
function isInsertTarget(beforePrev: Item | undefined): boolean {
|
||||
return beforePrev?.kind === "word" && (beforePrev.upper === "INTO" || beforePrev.upper === "TABLE" || beforePrev.upper === "EXISTS");
|
||||
return (
|
||||
beforePrev?.kind === "word" &&
|
||||
(beforePrev.upper === "INTO" || beforePrev.upper === "TABLE" || beforePrev.upper === "EXISTS")
|
||||
);
|
||||
}
|
||||
|
||||
function isCreateTable(items: Item[]): boolean {
|
||||
const words = items.filter((t) => t.kind === "word").slice(0, 3).map((t) => t.upper);
|
||||
const words = items
|
||||
.filter((t) => t.kind === "word")
|
||||
.slice(0, 3)
|
||||
.map((t) => t.upper);
|
||||
return words[0] === "CREATE" && words.includes("TABLE");
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,11 @@ export function findParams(text: string, driver: DbDriver, numbered = 0): SqlPar
|
||||
}
|
||||
|
||||
/** `text` with each parameter replaced by its value (empty: `NULL`). */
|
||||
export function bindParams(text: string, params: readonly SqlParam[], values: Readonly<Record<string, string>>): string {
|
||||
export function bindParams(
|
||||
text: string,
|
||||
params: readonly SqlParam[],
|
||||
values: Readonly<Record<string, string>>,
|
||||
): string {
|
||||
let out = "";
|
||||
let at = 0;
|
||||
for (const p of params) {
|
||||
|
||||
@@ -52,10 +52,16 @@ const NOT_COLUMNS = new Set(
|
||||
const eq = (a: string | null | undefined, b: string | null | undefined) =>
|
||||
!!a && !!b && a.toLowerCase() === b.toLowerCase();
|
||||
|
||||
export function findTable(model: DbCompletion | null, schema: string | null, name: string): DbCompletionTable | undefined {
|
||||
export function findTable(
|
||||
model: DbCompletion | null,
|
||||
schema: string | null,
|
||||
name: string,
|
||||
): DbCompletionTable | undefined {
|
||||
const tables = model?.tables ?? [];
|
||||
if (schema) return tables.find((t) => eq(t.schema, schema) && eq(t.name, name));
|
||||
return tables.find((t) => eq(t.schema, model?.defaultSchema) && eq(t.name, name)) ?? tables.find((t) => eq(t.name, name));
|
||||
return (
|
||||
tables.find((t) => eq(t.schema, model?.defaultSchema) && eq(t.name, name)) ?? tables.find((t) => eq(t.name, name))
|
||||
);
|
||||
}
|
||||
|
||||
const isName = (t: Token | undefined): t is Token => !!t && (t.kind === "word" || t.kind === "quoted");
|
||||
@@ -223,7 +229,9 @@ export class SqlResolver {
|
||||
clause !== "values" &&
|
||||
clause !== "limit" &&
|
||||
!this.outputAliases(i).has(name.toLowerCase());
|
||||
return judged ? { ...span, symbol: null, problem: `Unable to resolve column '${name}'` } : { ...span, symbol: null };
|
||||
return judged
|
||||
? { ...span, symbol: null, problem: `Unable to resolve column '${name}'` }
|
||||
: { ...span, symbol: null };
|
||||
}
|
||||
|
||||
/** Every unresolved name of the text. */
|
||||
@@ -273,7 +281,12 @@ export class SqlResolver {
|
||||
const t = toks[k]!;
|
||||
if (!isName(t)) continue;
|
||||
const prev = toks[k - 1]!;
|
||||
if ((prev.kind === "word" && prev.upper === "AS") || isP(prev, ")") || prev.kind === "number" || prev.kind === "string")
|
||||
if (
|
||||
(prev.kind === "word" && prev.upper === "AS") ||
|
||||
isP(prev, ")") ||
|
||||
prev.kind === "number" ||
|
||||
prev.kind === "string"
|
||||
)
|
||||
out.add(identName(t).toLowerCase());
|
||||
}
|
||||
return out;
|
||||
|
||||
@@ -245,7 +245,9 @@ describe("completion candidates", () => {
|
||||
insert: "id, email, name, created_at",
|
||||
});
|
||||
const two = complete("select | from users u join orders o on o.user_id = u.id");
|
||||
expect(two.find((x) => x.kind === "columns" && x.tail === "(u)")?.insert).toBe("u.id, u.email, u.name, u.created_at");
|
||||
expect(two.find((x) => x.kind === "columns" && x.tail === "(u)")?.insert).toBe(
|
||||
"u.id, u.email, u.name, u.created_at",
|
||||
);
|
||||
});
|
||||
|
||||
it("names unaliased tables by schema in column rows", () => {
|
||||
@@ -320,7 +322,10 @@ describe("VALUES parameter info", () => {
|
||||
return valuesSlot(sql.slice(0, pos) + sql.slice(pos + 1), pos, "sqlite");
|
||||
};
|
||||
it("finds the column of the value at the caret", () => {
|
||||
expect(slot("insert into users (email, name) values ('a', |)")).toMatchObject({ columns: ["email", "name"], index: 1 });
|
||||
expect(slot("insert into users (email, name) values ('a', |)")).toMatchObject({
|
||||
columns: ["email", "name"],
|
||||
index: 1,
|
||||
});
|
||||
expect(slot("insert into users values (|)")).toMatchObject({ columns: null, index: 0 });
|
||||
expect(slot("insert into users (email) values ('a'), (|")).toMatchObject({ index: 0 });
|
||||
expect(slot("insert into users (email|)")).toBeNull();
|
||||
@@ -359,7 +364,9 @@ describe("resolve", () => {
|
||||
});
|
||||
|
||||
it("leaves names alone when it cannot judge them", () => {
|
||||
expect(problems("select foo from unknown_table;")).toEqual(["unknown_table: Unable to resolve table 'unknown_table'"]);
|
||||
expect(problems("select foo from unknown_table;")).toEqual([
|
||||
"unknown_table: Unable to resolve table 'unknown_table'",
|
||||
]);
|
||||
expect(problems("select * from (select * from users) x where x.whatever = 1;")).toEqual([]);
|
||||
expect(problems("select rowid, * from users;")).toEqual([]);
|
||||
});
|
||||
@@ -384,7 +391,12 @@ describe("params", () => {
|
||||
it("finds parameters per dialect and binds values", () => {
|
||||
const names = (sql: string, driver: "sqlite" | "postgres" | "mysql" = "sqlite", n = 0) =>
|
||||
findParams(sql, driver, n).map((p) => p.name);
|
||||
expect(names("select * from t where a = :id and b = ? and c = ?3 and d = @x and e = '?' -- ?")).toEqual([":id", "?1", "?3", "@x"]);
|
||||
expect(names("select * from t where a = :id and b = ? and c = ?3 and d = @x and e = '?' -- ?")).toEqual([
|
||||
":id",
|
||||
"?1",
|
||||
"?3",
|
||||
"@x",
|
||||
]);
|
||||
expect(names("select * from t where a = ? and b = ?", "sqlite", 2)).toEqual(["?3", "?4"]);
|
||||
expect(names("select @x := 1, @@version, ? from t", "mysql")).toEqual(["?1"]);
|
||||
expect(names("select j ? 'k', $1, a::int from t where b = :name", "postgres")).toEqual(["$1", ":name"]);
|
||||
|
||||
Reference in new issue
Заблокувати користувача