inline-sql-toolkit 0.5.1 → 0.5.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.3 - 2026-10-10
4
+
5
+ - Feature: a "# inline-sql: skip" comment excludes a specific SQL literal from formatting (as a trailing comment on the closing quote line or on a dedicated line directly preceding the literal).
6
+
7
+ ## 0.5.2 - 2026-10-09
8
+
9
+ - Fixed: with `inlineSql.format.commaPosition: "before"`, the rest of a
10
+ multi-line item (`CASE … END`, a subquery, a wrapped function call) now
11
+ moves right with its leading `, `, so `END` and the closing `)` no longer
12
+ line up with the comma. Only lines up to the `)` / `]` / `END` that closes
13
+ the item move; a following `JOIN` or statement stays put. Nested items add
14
+ up; lines that continue a string or block comment are left untouched.
15
+
3
16
  ## 0.5.1 - 2026-10-02
4
17
 
5
18
  - Security: pins patched versions of OSV-flagged transitive dependencies
package/README.md CHANGED
@@ -246,6 +246,41 @@ order, or the candidate is skipped (`FORMATTER_FAILED`).
246
246
  A single-quoted literal stays on one line and keeps its leading and trailing
247
247
  spaces, because the string may be joined to other text at runtime.
248
248
 
249
+ ### Skipping a literal
250
+
251
+ To exclude a specific SQL literal from formatting, add an `# inline-sql: skip` pragma comment (case-insensitive, matched with `/\binline-sql:\s*skip(?![\w-])/i`). Other comments such as `# noqa: E501` or `# type: ignore` may share the same comment line.
252
+
253
+ You can place the pragma in either of two ways:
254
+
255
+ 1. **Trailing comment on the closing line**: on the line where the literal ends (the closing quote line).
256
+ ```python
257
+ q = "select a,b from t" # inline-sql: skip
258
+ q = """--sql
259
+ select a,
260
+ b
261
+ from t
262
+ """ # inline-sql: skip
263
+ ```
264
+ 2. **Dedicated comment line immediately preceding the literal**: on the line directly above where the literal starts, containing only whitespace followed by the `# inline-sql: skip` comment.
265
+ ```python
266
+ # inline-sql: skip
267
+ q = """--sql
268
+ ...
269
+ """
270
+ query = (
271
+ # inline-sql: skip
272
+ """--sql ..."""
273
+ )
274
+ ```
275
+
276
+ Notes on skipping behavior:
277
+
278
+ - On triple-quoted strings, any text after the opening quotes (such as `"""--sql # inline-sql: skip`) is part of the string body, not a Python comment, so a pragma placed there has no effect.
279
+ - A trailing comment on a preceding statement (e.g. `a = "select 1" # inline-sql: skip`) does not affect a literal starting on the next line (`b = "select 2"`). Dedicated comment lines must consist only of whitespace and the comment.
280
+ - Syntax highlighting is powered by TextMate grammar and is not affected by `# inline-sql: skip`. Only formatting excludes the literal.
281
+ - When running **Format at Cursor** on a skipped literal, the command reports `NO_SQL_CANDIDATE`.
282
+ - Both CLI `--check` and `--write` honor `# inline-sql: skip` with the exact same rules: skipped literals are never formatted, never reported on stderr as skipped candidates, and do not cause `--check` to fail.
283
+
249
284
  ## Trust, privacy, and offline behavior
250
285
 
251
286
  In an untrusted workspace the extension provides highlighting only. The three
@@ -268,7 +303,8 @@ over the network, passed to a shell/database, or executed. The bundled
268
303
  `INVALID_CONFIGURATION`, or `PROCESS_FAILED`).
269
304
  - **SQL looks different than expected:** formatting does not validate SQL or
270
305
  infer a dialect. Adjust the settings above and review the source-level
271
- candidate before applying the one-step edit.
306
+ candidate before applying the one-step edit, or exclude the literal with
307
+ `# inline-sql: skip`.
272
308
 
273
309
  For security reporting, see [SECURITY.md](SECURITY.md). For source-free bug
274
310
  reports and diagnostic reason codes, see [SUPPORT.md](SUPPORT.md). Licensing and
package/dist/cli.js CHANGED
@@ -808,6 +808,7 @@ function stringSurfaceAt(source, index) {
808
808
  }
809
809
  function scanSource(source) {
810
810
  const surfaces = [];
811
+ const comments = [];
811
812
  let unterminated = false;
812
813
  let index = 0;
813
814
  while (index < source.length) {
@@ -818,6 +819,7 @@ function scanSource(source) {
818
819
  }
819
820
  if (char === "#") {
820
821
  const end = source.indexOf("\n", index);
822
+ comments.push(new SourceSpan(index, end === -1 ? source.length : end));
821
823
  index = end === -1 ? source.length : end;
822
824
  continue;
823
825
  }
@@ -837,7 +839,7 @@ function scanSource(source) {
837
839
  }
838
840
  index++;
839
841
  }
840
- return { surfaces, unterminated };
842
+ return { surfaces, comments, unterminated };
841
843
  }
842
844
  function scanFstringFields(source, contentSpan) {
843
845
  const fields = [];
@@ -919,6 +921,7 @@ function fstringKind(prefix) {
919
921
  }
920
922
 
921
923
  // src/python-analysis/literals.ts
924
+ var SKIP_PRAGMA = /\binline-sql:\s*skip(?![\w-])/i;
922
925
  function onlyWhitespace(text) {
923
926
  return /^[\s]*$/.test(text);
924
927
  }
@@ -941,7 +944,25 @@ function isConcatenated(source, surfaces, index) {
941
944
  }
942
945
  function analyzeDocument(source) {
943
946
  const sourceMap = SourceMap.fromText(source);
944
- const { surfaces, unterminated } = scanSource(source);
947
+ const { surfaces, comments, unterminated } = scanSource(source);
948
+ const trailingPragmas = /* @__PURE__ */ new Set();
949
+ const ownLinePragmas = /* @__PURE__ */ new Set();
950
+ for (const span of comments) {
951
+ const commentText2 = source.slice(span.start, span.end);
952
+ if (!SKIP_PRAGMA.test(commentText2)) continue;
953
+ const line = sourceMap.vscodeFromOffset(span.start).line;
954
+ const lineStartOffset = sourceMap.offsetFromVscode(line, 0);
955
+ const prefix = source.slice(lineStartOffset, span.start);
956
+ if (/^[ \t]*$/.test(prefix)) {
957
+ ownLinePragmas.add(line);
958
+ } else {
959
+ trailingPragmas.add(line);
960
+ }
961
+ }
962
+ const skipPragmaLines = {
963
+ trailing: trailingPragmas,
964
+ ownLine: ownLinePragmas
965
+ };
945
966
  const supported = [];
946
967
  const unsupported = [];
947
968
  surfaces.forEach((surface, index) => {
@@ -1005,7 +1026,8 @@ function analyzeDocument(source) {
1005
1026
  sourceMap,
1006
1027
  unterminated,
1007
1028
  supported: [...supported].sort((left, right) => left.span.start - right.span.start),
1008
- unsupported: [...unsupported].sort((left, right) => left.span.start - right.span.start)
1029
+ unsupported: [...unsupported].sort((left, right) => left.span.start - right.span.start),
1030
+ skipPragmaLines
1009
1031
  };
1010
1032
  }
1011
1033
 
@@ -1907,8 +1929,9 @@ function wrappingComma(line) {
1907
1929
  if (comma < 0) return -1;
1908
1930
  return /^[ \t]*(?:--.*)?$/.test(line.slice(comma + 1)) ? comma : -1;
1909
1931
  }
1910
- function moveCommasToLineStarts(text) {
1932
+ function moveCommasToLineStarts(text, dialect) {
1911
1933
  const lines = text.split("\n");
1934
+ const inserts = /* @__PURE__ */ new Map();
1912
1935
  for (let index = 0; index + 1 < lines.length; index++) {
1913
1936
  const line = lines[index] ?? "";
1914
1937
  const next = lines[index + 1] ?? "";
@@ -1920,8 +1943,43 @@ function moveCommasToLineStarts(text) {
1920
1943
  const content = nextParts?.[2] ?? "";
1921
1944
  lines[index] = `${line.slice(0, comma)}${line.slice(comma + 1)}`.trimEnd();
1922
1945
  lines[index + 1] = `${indent}, ${content}`;
1946
+ inserts.set(index + 1, indent.length);
1947
+ }
1948
+ if (inserts.size === 0) return text;
1949
+ return shiftMovedItems(lines, inserts, dialect).join("\n");
1950
+ }
1951
+ function shiftMovedItems(lines, inserts, dialect) {
1952
+ const starts = [];
1953
+ let offset = 0;
1954
+ for (const line of lines) {
1955
+ starts.push(offset);
1956
+ offset += line.length + 1;
1957
+ }
1958
+ const tokens = lexSql(lines.join("\n"), dialect);
1959
+ const textLines = /* @__PURE__ */ new Set();
1960
+ for (const token of tokens) {
1961
+ if (token.kind === "space" || !token.text.includes("\n")) continue;
1962
+ starts.forEach((start, line) => {
1963
+ if (start > token.start && start < token.end) textLines.add(line);
1964
+ });
1965
+ }
1966
+ const shifts = new Array(lines.length).fill(0);
1967
+ for (const [target, column] of inserts) {
1968
+ for (let index = target + 1; index < lines.length; index++) {
1969
+ const line = lines[index] ?? "";
1970
+ const indent = line.length - line.trimStart().length;
1971
+ if (indent === line.length || textLines.has(index)) continue;
1972
+ if (indent < column) break;
1973
+ if (indent === column && !/^(?:[)\]]|END\b)/i.test(line.slice(indent))) break;
1974
+ shifts[index] = (shifts[index] ?? 0) + 2;
1975
+ }
1923
1976
  }
1924
- return lines.join("\n");
1977
+ return lines.map((line, index) => {
1978
+ const shift = shifts[index] ?? 0;
1979
+ if (shift === 0) return line;
1980
+ const indent = line.length - line.trimStart().length;
1981
+ return `${line.slice(0, indent)}${" ".repeat(shift)}${line.slice(indent)}`;
1982
+ });
1925
1983
  }
1926
1984
  var DOLLAR_QUOTE = /\$[A-Za-z_][A-Za-z0-9_]*\$|\$\$/y;
1927
1985
  function rejoinFunctionCalls(text) {
@@ -2047,7 +2105,7 @@ function formatOnce(analysis, literal, detection, options, nonce, sqlFormatter,
2047
2105
  formatted = rejoinFunctionCalls(formatted);
2048
2106
  }
2049
2107
  if (options.commaPosition === "before") {
2050
- formatted = moveCommasToLineStarts(formatted);
2108
+ formatted = moveCommasToLineStarts(formatted, options.dialect);
2051
2109
  }
2052
2110
  formatted = breakTrailingDistributeLines(formatted, options.dialect);
2053
2111
  const difference = sqlTokenDifference(plan.protectedSql, formatted, options.dialect);
@@ -2175,6 +2233,12 @@ var MAX_CANDIDATE_BYTES = 1024 * 1024;
2175
2233
  var MAX_CANDIDATES = 1e3;
2176
2234
  var ResourceLimitError = class extends Error {
2177
2235
  };
2236
+ function isSkippedByPragma(literal, analysis) {
2237
+ const startLine = analysis.sourceMap.vscodeFromOffset(literal.span.start).line;
2238
+ const endLine = analysis.sourceMap.vscodeFromOffset(literal.span.end).line;
2239
+ const { trailing, ownLine } = analysis.skipPragmaLines;
2240
+ return ownLine.has(startLine - 1) || trailing.has(endLine) || ownLine.has(endLine);
2241
+ }
2178
2242
  function discover(analysis) {
2179
2243
  const literals = [
2180
2244
  ...analysis.supported,
@@ -2183,7 +2247,7 @@ function discover(analysis) {
2183
2247
  const units = literals.map((literal) => ({
2184
2248
  literal,
2185
2249
  detection: detectSql(literal, analysis.sourceMap)
2186
- })).filter((unit) => unit.detection.matched);
2250
+ })).filter((unit) => unit.detection.matched && !isSkippedByPragma(unit.literal, analysis));
2187
2251
  return [...units].sort((left, right) => left.literal.span.start - right.literal.span.start);
2188
2252
  }
2189
2253
  function selectUnits(units, target, sourceMap) {
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "bugs": {
12
12
  "url": "https://github.com/hidenobunagai/inline-sql-toolkit/issues"
13
13
  },
14
- "version": "0.5.1",
14
+ "version": "0.5.3",
15
15
  "icon": "icon.png",
16
16
  "type": "module",
17
17
  "bin": {