@rohal12/spindle-lsp 0.5.3 → 0.6.0

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/bin.js CHANGED
@@ -1193,7 +1193,8 @@ var init_diagnostic_codes = __esm({
1193
1193
  BrokenPassageLink: "SP300",
1194
1194
  WidgetArgCountMismatch: "SP301",
1195
1195
  DeadEndPassage: "SP400",
1196
- UnreachablePassage: "SP401"
1196
+ UnreachablePassage: "SP401",
1197
+ LineTooLong: "SP500"
1197
1198
  };
1198
1199
  severityMap = {
1199
1200
  SP100: "warning",
@@ -1213,7 +1214,8 @@ var init_diagnostic_codes = __esm({
1213
1214
  SP300: "warning",
1214
1215
  SP301: "warning",
1215
1216
  SP400: "hint",
1216
- SP401: "hint"
1217
+ SP401: "hint",
1218
+ SP500: "warning"
1217
1219
  };
1218
1220
  }
1219
1221
  });
@@ -2092,7 +2094,7 @@ var init_link_parser = __esm({
2092
2094
  });
2093
2095
 
2094
2096
  // src/plugins/diagnostics.ts
2095
- function computeDiagnostics(uri, workspace) {
2097
+ function computeDiagnostics(uri, workspace, options) {
2096
2098
  if (!workspace.initialized) return [];
2097
2099
  try {
2098
2100
  const text = workspace.documents.getText(uri);
@@ -2124,6 +2126,12 @@ function computeDiagnostics(uri, workspace) {
2124
2126
  validateWidgetInvocations(macros, workspace, diagnostics);
2125
2127
  } catch {
2126
2128
  }
2129
+ if (options?.maxLineLength) {
2130
+ try {
2131
+ validateLineLength(text, passages, options.maxLineLength, diagnostics);
2132
+ } catch {
2133
+ }
2134
+ }
2127
2135
  return diagnostics;
2128
2136
  } catch {
2129
2137
  return [];
@@ -2342,6 +2350,26 @@ function validateWidgetInvocations(macros, workspace, diagnostics) {
2342
2350
  }
2343
2351
  }
2344
2352
  }
2353
+ function validateLineLength(text, passages, maxLength, diagnostics) {
2354
+ const excludedTags = /* @__PURE__ */ new Set(["script", "stylesheet"]);
2355
+ const excludedPassages = new Set(passages.filter((p) => p.tags?.some((t) => excludedTags.has(t))).map((p) => p.name));
2356
+ const lines = text.split("\n");
2357
+ for (let i = 0; i < lines.length; i++) {
2358
+ const line = lines[i];
2359
+ if (/^::\s+/.test(line)) continue;
2360
+ if (/^\s*<[a-zA-Z]/.test(line)) continue;
2361
+ if (line.length > maxLength) {
2362
+ diagnostics.push(makeDiag(
2363
+ {
2364
+ start: { line: i, character: maxLength },
2365
+ end: { line: i, character: line.length }
2366
+ },
2367
+ DiagnosticCode.LineTooLong,
2368
+ `Line exceeds ${maxLength} characters (${line.length})`
2369
+ ));
2370
+ }
2371
+ }
2372
+ }
2345
2373
  function makeDiag(range, code, message) {
2346
2374
  return {
2347
2375
  range,
@@ -2639,6 +2667,7 @@ function parseArgs(args2) {
2639
2667
  format: "pretty",
2640
2668
  severity: null,
2641
2669
  configPath: null,
2670
+ maxLineLength: null,
2642
2671
  patterns: []
2643
2672
  };
2644
2673
  let i = 0;
@@ -2674,6 +2703,14 @@ function parseArgs(args2) {
2674
2703
  } else if (arg.startsWith("--config=")) {
2675
2704
  options.configPath = arg.slice("--config=".length);
2676
2705
  i++;
2706
+ } else if (arg === "--max-line-length" && i + 1 < args2.length) {
2707
+ const n = parseInt(args2[i + 1], 10);
2708
+ if (!isNaN(n) && n > 0) options.maxLineLength = n;
2709
+ i += 2;
2710
+ } else if (arg.startsWith("--max-line-length=")) {
2711
+ const n = parseInt(arg.slice("--max-line-length=".length), 10);
2712
+ if (!isNaN(n) && n > 0) options.maxLineLength = n;
2713
+ i++;
2677
2714
  } else if (!arg.startsWith("--")) {
2678
2715
  options.patterns.push(arg);
2679
2716
  i++;
@@ -2751,10 +2788,14 @@ async function runCheck(args2) {
2751
2788
  if (Object.keys(projectConfig.macros).length > 0) {
2752
2789
  workspace.macros.loadSupplements(projectConfig.macros);
2753
2790
  }
2791
+ const diagOptions = {};
2792
+ if (options.maxLineLength !== null) {
2793
+ diagOptions.maxLineLength = options.maxLineLength;
2794
+ }
2754
2795
  const allResults = [];
2755
2796
  let hasErrors = false;
2756
2797
  for (const uri of fileContents.keys()) {
2757
- let diags = computeDiagnostics(uri, workspace);
2798
+ let diags = computeDiagnostics(uri, workspace, diagOptions);
2758
2799
  if (options.severity) {
2759
2800
  diags = filterBySeverity(diags, options.severity);
2760
2801
  }
@@ -3209,6 +3250,13 @@ function indentMacros(lines, isBlock, isDedenting) {
3209
3250
  if (openMatch && isBlock(openMatch[1])) {
3210
3251
  indentLevel++;
3211
3252
  }
3253
+ const closeGlobal = /\{\/[A-Za-z][\w-]*\s*\}/g;
3254
+ let cm;
3255
+ while ((cm = closeGlobal.exec(trimmed)) !== null) {
3256
+ if (cm.index > 0) {
3257
+ indentLevel = Math.max(0, indentLevel - 1);
3258
+ }
3259
+ }
3212
3260
  }
3213
3261
  return result;
3214
3262
  }
@@ -5589,8 +5637,34 @@ var init_server2 = __esm({
5589
5637
  });
5590
5638
 
5591
5639
  // src/bin.ts
5640
+ import { createRequire as createRequire2 } from "node:module";
5592
5641
  var args = process.argv.slice(2);
5593
- if (args[0] === "check") {
5642
+ var VERSION = createRequire2(import.meta.url)("../package.json").version;
5643
+ var HELP = `spindle-lsp v${VERSION}
5644
+ Language Server Protocol server for Spindle story format
5645
+
5646
+ Usage:
5647
+ spindle-lsp Start the LSP server (stdio)
5648
+ spindle-lsp check <files> Lint .twee/.tw files
5649
+ spindle-lsp format <files> Format .twee/.tw files in place
5650
+ spindle-lsp mcp Start the MCP server
5651
+
5652
+ Options:
5653
+ --help, -h Show this help message
5654
+ --version, -v Show version number
5655
+
5656
+ Check options:
5657
+ --format <pretty|json|sarif> Output format (default: pretty)
5658
+ --severity <level> Minimum severity: error, warning, info, hint
5659
+ --config <path> Path to config file
5660
+ --max-line-length <n> Warn on lines exceeding n characters`;
5661
+ if (args.includes("--help") || args.includes("-h") || args.length === 0) {
5662
+ console.log(HELP);
5663
+ process.exit(0);
5664
+ } else if (args.includes("--version") || args.includes("-v")) {
5665
+ console.log(VERSION);
5666
+ process.exit(0);
5667
+ } else if (args[0] === "check") {
5594
5668
  const { runCheck: runCheck2 } = await Promise.resolve().then(() => (init_check(), check_exports));
5595
5669
  const exitCode = await runCheck2(args.slice(1));
5596
5670
  process.exit(exitCode);