@jbearak/sight 0.12.0 → 0.14.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.
@@ -52,7 +52,7 @@ var package_default;
52
52
  var init_package = __esm(() => {
53
53
  package_default = {
54
54
  name: "@jbearak/sight",
55
- version: "0.12.0",
55
+ version: "0.14.0",
56
56
  type: "module",
57
57
  workspaces: [
58
58
  "client"
@@ -633,6 +633,13 @@ function warn_unknown_key(key_path, raw_key, warn) {
633
633
  message: `Unknown project config key '${key_path}.${raw_key}'; ignoring`
634
634
  });
635
635
  }
636
+ function warn_compat_collision(canonical_path, alias_path, warn) {
637
+ warn?.({
638
+ code: "normalized-key-collision",
639
+ key_path: canonical_path,
640
+ message: `Both '${canonical_path}' and compatibility alias '${alias_path}' ` + `are set; using '${canonical_path}'`
641
+ });
642
+ }
636
643
  function normalize_severity_value(value, key_path, allowed, warn) {
637
644
  if (typeof value !== "string") {
638
645
  warn_invalid_value(key_path, value, warn);
@@ -734,25 +741,40 @@ function maybe_assign_object(target, key, value) {
734
741
  }
735
742
  }
736
743
  function map_diagnostics(raw, warn) {
737
- warn_unknown_keys(raw, ["enabled", "indentation", "severity"], "diagnostics", warn);
744
+ warn_unknown_keys(raw, ["enabled", "indentation", "severity", "undefinedVariableSeverity"], "diagnostics", warn);
738
745
  const mapped = {};
739
746
  assign_boolean(mapped, "enabled", raw, "enabled", "diagnostics.enabled", warn);
740
747
  assign_boolean(mapped, "indentation", raw, "indentation", "diagnostics.indentation", warn);
741
748
  const severity = object_value(raw, "severity", "diagnostics.severity", warn);
749
+ const mapped_severity = {};
750
+ let canonical_undefined_variable;
742
751
  if (severity) {
743
752
  warn_unknown_keys(severity, DIAGNOSTIC_SEVERITY_KEYS, "diagnostics.severity", warn);
744
- const mapped_severity = {};
745
753
  for (const my_key of DIAGNOSTIC_SEVERITY_KEYS) {
746
754
  const value = pick_key(severity, my_key, warn, `diagnostics.severity.${my_key}`);
747
755
  if (value !== undefined) {
756
+ if (my_key === "undefinedVariable") {
757
+ canonical_undefined_variable = value;
758
+ }
748
759
  const normalized = normalize_severity_value(value, `diagnostics.severity.${my_key}`, SEVERITIES, warn);
749
760
  if (normalized) {
750
761
  mapped_severity[my_key] = normalized;
751
762
  }
752
763
  }
753
764
  }
754
- maybe_assign_object(mapped, "severity", mapped_severity);
755
765
  }
766
+ const legacy_undefined_variable = pick_key(raw, "undefinedVariableSeverity", warn, "diagnostics.undefinedVariableSeverity");
767
+ if (legacy_undefined_variable !== undefined) {
768
+ if (canonical_undefined_variable !== undefined) {
769
+ warn_compat_collision("diagnostics.severity.undefinedVariable", "diagnostics.undefinedVariableSeverity", warn);
770
+ } else {
771
+ const normalized = normalize_severity_value(legacy_undefined_variable, "diagnostics.undefinedVariableSeverity", SEVERITIES, warn);
772
+ if (normalized) {
773
+ mapped_severity.undefinedVariable = normalized;
774
+ }
775
+ }
776
+ }
777
+ maybe_assign_object(mapped, "severity", mapped_severity);
756
778
  return Object.keys(mapped).length > 0 ? mapped : undefined;
757
779
  }
758
780
  function map_formatting(raw, warn) {
@@ -800,7 +822,12 @@ function map_cross_file(raw, warn) {
800
822
  "maxForwardDepth",
801
823
  "maxChainDepth",
802
824
  "maxCalleeRevalidations",
803
- "diagnostics"
825
+ "maxCachedFiles",
826
+ "maxCachedScopes",
827
+ "maxCachedForwardClosures",
828
+ "diagnostics",
829
+ "missingFileSeverity",
830
+ "caseMismatchSeverity"
804
831
  ], "crossFile", warn);
805
832
  const mapped = {};
806
833
  assign_boolean(mapped, "index_workspace", raw, "indexWorkspace", "crossFile.indexWorkspace", warn);
@@ -811,10 +838,15 @@ function map_cross_file(raw, warn) {
811
838
  assign_number(mapped, "max_forward_depth", raw, "maxForwardDepth", "crossFile.maxForwardDepth", warn);
812
839
  assign_number(mapped, "max_chain_depth", raw, "maxChainDepth", "crossFile.maxChainDepth", warn);
813
840
  assign_number(mapped, "max_callee_revalidations", raw, "maxCalleeRevalidations", "crossFile.maxCalleeRevalidations", warn);
841
+ assign_number(mapped, "max_cached_files", raw, "maxCachedFiles", "crossFile.maxCachedFiles", warn);
842
+ assign_number(mapped, "max_cached_scopes", raw, "maxCachedScopes", "crossFile.maxCachedScopes", warn);
843
+ assign_number(mapped, "max_cached_forward_closures", raw, "maxCachedForwardClosures", "crossFile.maxCachedForwardClosures", warn);
814
844
  const diagnostics = object_value(raw, "diagnostics", "crossFile.diagnostics", warn);
845
+ const mapped_diagnostics = {};
846
+ let canonical_missing_file;
847
+ let canonical_case_mismatch;
815
848
  if (diagnostics) {
816
849
  warn_unknown_keys(diagnostics, ["missingFile", "maxDepth", "callSiteIdentification", "caseMismatch"], "crossFile.diagnostics", warn);
817
- const mapped_diagnostics = {};
818
850
  for (const [public_key, internal_key] of [
819
851
  ["missingFile", "missing_file"],
820
852
  ["maxDepth", "max_depth"],
@@ -823,6 +855,9 @@ function map_cross_file(raw, warn) {
823
855
  const key_path = `crossFile.diagnostics.${public_key}`;
824
856
  const value = pick_key(diagnostics, public_key, warn, key_path);
825
857
  if (value !== undefined) {
858
+ if (public_key === "missingFile") {
859
+ canonical_missing_file = value;
860
+ }
826
861
  const normalized = normalize_severity_value(value, key_path, CROSS_FILE_SEVERITIES, warn);
827
862
  if (normalized) {
828
863
  mapped_diagnostics[internal_key] = normalized;
@@ -832,13 +867,36 @@ function map_cross_file(raw, warn) {
832
867
  const case_mismatch_key_path = "crossFile.diagnostics.caseMismatch";
833
868
  const case_mismatch_value = pick_key(diagnostics, "caseMismatch", warn, case_mismatch_key_path);
834
869
  if (case_mismatch_value !== undefined) {
870
+ canonical_case_mismatch = case_mismatch_value;
835
871
  const normalized = normalize_severity_value(case_mismatch_value, case_mismatch_key_path, CROSS_FILE_CASE_MISMATCH_SEVERITIES, warn);
836
872
  if (normalized) {
837
873
  mapped_diagnostics["case_mismatch"] = normalized;
838
874
  }
839
875
  }
840
- maybe_assign_object(mapped, "diagnostics", mapped_diagnostics);
841
876
  }
877
+ const legacy_missing_file = pick_key(raw, "missingFileSeverity", warn, "crossFile.missingFileSeverity");
878
+ if (legacy_missing_file !== undefined) {
879
+ if (canonical_missing_file !== undefined) {
880
+ warn_compat_collision("crossFile.diagnostics.missingFile", "crossFile.missingFileSeverity", warn);
881
+ } else {
882
+ const normalized = normalize_severity_value(legacy_missing_file, "crossFile.missingFileSeverity", CROSS_FILE_SEVERITIES, warn);
883
+ if (normalized) {
884
+ mapped_diagnostics.missing_file = normalized;
885
+ }
886
+ }
887
+ }
888
+ const legacy_case_mismatch = pick_key(raw, "caseMismatchSeverity", warn, "crossFile.caseMismatchSeverity");
889
+ if (legacy_case_mismatch !== undefined) {
890
+ if (canonical_case_mismatch !== undefined) {
891
+ warn_compat_collision("crossFile.diagnostics.caseMismatch", "crossFile.caseMismatchSeverity", warn);
892
+ } else {
893
+ const normalized = normalize_severity_value(legacy_case_mismatch, "crossFile.caseMismatchSeverity", CROSS_FILE_CASE_MISMATCH_SEVERITIES, warn);
894
+ if (normalized) {
895
+ mapped_diagnostics.case_mismatch = normalized;
896
+ }
897
+ }
898
+ }
899
+ maybe_assign_object(mapped, "diagnostics", mapped_diagnostics);
842
900
  return Object.keys(mapped).length > 0 ? mapped : undefined;
843
901
  }
844
902
  function map_public_config_to_partial_config(raw, warn) {
@@ -857,12 +915,21 @@ function map_public_config_to_partial_config(raw, warn) {
857
915
  warn_invalid_value("adoPaths", ado_paths, warn);
858
916
  }
859
917
  }
860
- const exclude = pick_key(raw, "exclude", warn, "exclude");
918
+ const workspace = object_value(raw, "workspace", "workspace", warn);
919
+ if (workspace) {
920
+ warn_unknown_keys(workspace, ["exclude"], "workspace", warn);
921
+ }
922
+ const canonical_exclude = workspace ? pick_key(workspace, "exclude", warn, "workspace.exclude") : undefined;
923
+ const legacy_exclude = pick_key(raw, "exclude", warn, "exclude");
924
+ const exclude = canonical_exclude !== undefined ? canonical_exclude : legacy_exclude;
925
+ if (canonical_exclude !== undefined && legacy_exclude !== undefined) {
926
+ warn_compat_collision("workspace.exclude", "exclude", warn);
927
+ }
861
928
  if (exclude !== undefined) {
862
929
  if (Array.isArray(exclude) && exclude.every((item) => typeof item === "string")) {
863
930
  mapped.exclude = exclude;
864
931
  } else {
865
- warn_invalid_value("exclude", exclude, warn);
932
+ warn_invalid_value(canonical_exclude !== undefined ? "workspace.exclude" : "exclude", exclude, warn);
866
933
  }
867
934
  }
868
935
  assign_string_member(root, "lineCommentStyle", raw, "lineCommentStyle", "lineCommentStyle", LINE_COMMENT_STYLES, warn);
@@ -941,6 +1008,7 @@ var init_schema = __esm(() => {
941
1008
  "indexWorkspace",
942
1009
  "adoPaths",
943
1010
  "exclude",
1011
+ "workspace",
944
1012
  "lineCommentStyle",
945
1013
  "debug",
946
1014
  "diagnostics",
@@ -12199,13 +12267,6 @@ class StataLexer {
12199
12267
  while (this.peek() === " " || this.peek() === "\t" || this.peek() === "\r") {
12200
12268
  this.advance();
12201
12269
  }
12202
- if (this.state.delimiterMode === "semicolon") {
12203
- return {
12204
- type: "WHITESPACE",
12205
- value: this.source.substring(this.position_to_offset(startLine, startColumn), this.position),
12206
- range: this.makeRange(startLine, startColumn, this.line, this.column)
12207
- };
12208
- }
12209
12270
  return null;
12210
12271
  }
12211
12272
  if (char === `
@@ -12214,17 +12275,11 @@ class StataLexer {
12214
12275
  return {
12215
12276
  type: "STATEMENT_TERMINATOR",
12216
12277
  value: `
12217
- `,
12218
- range: this.makeRange(startLine, startColumn, this.line, this.column)
12219
- };
12220
- } else {
12221
- return {
12222
- type: "WHITESPACE",
12223
- value: `
12224
12278
  `,
12225
12279
  range: this.makeRange(startLine, startColumn, this.line, this.column)
12226
12280
  };
12227
12281
  }
12282
+ return null;
12228
12283
  }
12229
12284
  if (char === ";") {
12230
12285
  if (this.state.delimiterMode === "semicolon") {
@@ -13205,6 +13260,34 @@ function is_swallowed_continuation_terminator(my_token, in_continuation) {
13205
13260
  }
13206
13261
 
13207
13262
  // src/parser/index.ts
13263
+ class ContinuationTracker {
13264
+ required_line = -1;
13265
+ prev_collected_line = null;
13266
+ prev_collected_end = null;
13267
+ chain_clean = true;
13268
+ note_continuation(continuation_token) {
13269
+ const cont_line = continuation_token.range.start.line;
13270
+ if (cont_line !== this.required_line) {
13271
+ return;
13272
+ }
13273
+ const cont_col = continuation_token.range.start.character;
13274
+ const is_first_on_prev_line = this.prev_collected_end !== null && this.required_line === this.prev_collected_line && cont_line === this.prev_collected_end.line;
13275
+ const has_leading_gap = is_first_on_prev_line ? cont_col > this.prev_collected_end.character : cont_col > 0;
13276
+ if (has_leading_gap) {
13277
+ this.chain_clean = false;
13278
+ }
13279
+ this.required_line += 1;
13280
+ }
13281
+ collect(token) {
13282
+ const token_line = token.range.start.line;
13283
+ const follows_continuation = this.prev_collected_line !== null && token_line > this.prev_collected_line && token_line === this.required_line && this.chain_clean;
13284
+ this.prev_collected_line = token_line;
13285
+ this.prev_collected_end = token.range.end;
13286
+ this.required_line = token_line;
13287
+ this.chain_clean = true;
13288
+ return follows_continuation;
13289
+ }
13290
+ }
13208
13291
  var PREFIX_COMMANDS, StataParser;
13209
13292
  var init_parser = __esm(() => {
13210
13293
  init_types();
@@ -13274,7 +13357,7 @@ var init_parser = __esm(() => {
13274
13357
  node = this.parseEmbeddedLanguageBlock("python");
13275
13358
  } else if (this.check("WORD") && this.isPrefixCommand(this.peek().value)) {
13276
13359
  node = this.parseCommand();
13277
- } else if (this.checkWord("program") && this.peekNext()?.value === "define") {
13360
+ } else if (this.checkWord("program") && this.tokens[this.current + 1]?.value === "define") {
13278
13361
  node = this.parseProgramDefinition();
13279
13362
  } else if (this.checkWord("syntax")) {
13280
13363
  node = this.parseSyntaxCommand();
@@ -13408,9 +13491,18 @@ var init_parser = __esm(() => {
13408
13491
  content += my_token.value;
13409
13492
  if (i < content_tokens.length - 1) {
13410
13493
  const next_token = content_tokens[i + 1];
13411
- const gap = next_token.range.start.character - my_token.range.end.character;
13412
- if (gap > 0) {
13413
- content += " ".repeat(gap);
13494
+ if (next_token.range.start.line === my_token.range.end.line) {
13495
+ const gap = next_token.range.start.character - my_token.range.end.character;
13496
+ if (gap > 0) {
13497
+ content += " ".repeat(gap);
13498
+ }
13499
+ } else if (next_token.range.start.line > my_token.range.end.line) {
13500
+ const line_gap = next_token.range.start.line - my_token.range.end.line;
13501
+ content += `
13502
+ `.repeat(line_gap);
13503
+ if (next_token.range.start.character > 0) {
13504
+ content += " ".repeat(next_token.range.start.character);
13505
+ }
13414
13506
  }
13415
13507
  }
13416
13508
  }
@@ -13449,12 +13541,20 @@ var init_parser = __esm(() => {
13449
13541
  const body = [];
13450
13542
  const was_inside_program = this.inside_program;
13451
13543
  this.inside_program = true;
13452
- while (!this.isAtEnd() && !this.checkWord("end")) {
13544
+ while (!this.isAtEnd()) {
13545
+ const my_trivia = this.collectTrivia();
13546
+ if (my_trivia.length > 0) {
13547
+ this.pending_trivia.push(...my_trivia);
13548
+ }
13549
+ if (this.checkWord("end") || this.isAtEnd()) {
13550
+ break;
13551
+ }
13453
13552
  const stmt = this.parseStatement();
13454
13553
  if (stmt) {
13455
13554
  body.push(stmt);
13456
13555
  }
13457
13556
  }
13557
+ const program_block_ending_trivia = this.checkWord("end") ? this.drainBlockEndingTrivia() : undefined;
13458
13558
  this.inside_program = was_inside_program;
13459
13559
  if (this.checkWord("end")) {
13460
13560
  this.advance();
@@ -13467,7 +13567,8 @@ var init_parser = __esm(() => {
13467
13567
  name: program_name,
13468
13568
  body,
13469
13569
  signature: merged_signature,
13470
- range: this.makeRange(start_token.range.start, this.previous().range.end)
13570
+ range: this.makeRange(start_token.range.start, this.previous().range.end),
13571
+ blockEndingTrivia: program_block_ending_trivia
13471
13572
  };
13472
13573
  }
13473
13574
  extract_and_merge_signatures(body) {
@@ -13538,20 +13639,20 @@ var init_parser = __esm(() => {
13538
13639
  let paren_depth = 0;
13539
13640
  const value_tokens = [];
13540
13641
  const preceded_by_continuation = [];
13541
- let continuation_pending = false;
13642
+ const continuation = new ContinuationTracker;
13542
13643
  while (!this.check("STATEMENT_TERMINATOR") && !this.isAtEnd()) {
13543
- if (this.skipContinuation()) {
13544
- continuation_pending = true;
13545
- continue;
13644
+ if (this.check("CONTINUATION")) {
13645
+ const cont_token = this.peek();
13646
+ if (this.skipContinuation()) {
13647
+ continuation.note_continuation(cont_token);
13648
+ continue;
13649
+ }
13546
13650
  }
13547
13651
  const token_type = this.peek().type;
13548
13652
  if (token_type === "COMMENT_LINE" || token_type === "COMMENT_BLOCK") {
13549
13653
  break;
13550
13654
  }
13551
13655
  const token = this.advance();
13552
- if (token.type === "WHITESPACE") {
13553
- continue;
13554
- }
13555
13656
  if (token.type === "LPAREN") {
13556
13657
  paren_depth++;
13557
13658
  } else if (token.type === "RPAREN") {
@@ -13562,8 +13663,7 @@ var init_parser = __esm(() => {
13562
13663
  }
13563
13664
  }
13564
13665
  value_tokens.push(token);
13565
- preceded_by_continuation.push(continuation_pending);
13566
- continuation_pending = false;
13666
+ preceded_by_continuation.push(continuation.collect(token));
13567
13667
  }
13568
13668
  const value = prefix_value + this.reconstruct_value_tokens(value_tokens, preceded_by_continuation);
13569
13669
  if (paren_depth > 0) {
@@ -13593,17 +13693,26 @@ var init_parser = __esm(() => {
13593
13693
  const function_name = this.advance().value;
13594
13694
  this.skipMacroDefinitionTrivia();
13595
13695
  const arg_tokens = [];
13696
+ const value_tokens = [];
13697
+ const preceded_by_continuation = [];
13698
+ const continuation = new ContinuationTracker;
13596
13699
  while (!this.check("STATEMENT_TERMINATOR") && !this.isAtEnd()) {
13597
- if (this.skipContinuation()) {
13598
- continue;
13700
+ if (this.check("CONTINUATION")) {
13701
+ const cont_token = this.peek();
13702
+ if (this.skipContinuation()) {
13703
+ continuation.note_continuation(cont_token);
13704
+ continue;
13705
+ }
13599
13706
  }
13600
13707
  if (this.check("COMMENT_LINE") || this.check("COMMENT_BLOCK")) {
13601
13708
  break;
13602
13709
  }
13603
13710
  const token = this.advance();
13604
13711
  arg_tokens.push(token);
13712
+ value_tokens.push(token);
13713
+ preceded_by_continuation.push(continuation.collect(token));
13605
13714
  }
13606
- const args_trimmed = this.reconstructTokensWithSpacing(arg_tokens).trim();
13715
+ const args_trimmed = this.reconstruct_value_tokens(value_tokens, preceded_by_continuation).trim();
13607
13716
  const extended_function = {
13608
13717
  name: function_name,
13609
13718
  args: args_trimmed,
@@ -13742,12 +13851,8 @@ var init_parser = __esm(() => {
13742
13851
  const has_condition_before = lbrace_token.range.start.line === command_start_line;
13743
13852
  this.validate_open_brace(lbrace_token, lbrace_index, has_condition_before);
13744
13853
  const body = [];
13745
- while (!this.check("RBRACE") && !this.isAtEnd()) {
13746
- const stmt = this.parseStatement();
13747
- if (stmt) {
13748
- body.push(stmt);
13749
- }
13750
- }
13854
+ const { body: my_body, blockEndingTrivia } = this.parseBraceBody();
13855
+ body.push(...my_body);
13751
13856
  if (this.check("RBRACE")) {
13752
13857
  const rbrace_index = this.current;
13753
13858
  const rbrace_token = this.advance();
@@ -13760,7 +13865,8 @@ var init_parser = __esm(() => {
13760
13865
  fullName: "{",
13761
13866
  expression: undefined,
13762
13867
  body,
13763
- range: this.makeRange(start_token.range.start, this.previous().range.end)
13868
+ range: this.makeRange(start_token.range.start, this.previous().range.end),
13869
+ blockEndingTrivia
13764
13870
  };
13765
13871
  }
13766
13872
  if (!this.check("WORD") && !this.check("MACRO_REF_LOCAL") && !this.check("MACRO_REF_GLOBAL")) {
@@ -13782,24 +13888,25 @@ var init_parser = __esm(() => {
13782
13888
  if (commandName === "args") {
13783
13889
  return this.parseArgsCommand(command_token, prefixes);
13784
13890
  }
13785
- if (commandName === "frame" && this.check("WORD")) {
13891
+ if (commandName === "frame") {
13786
13892
  const saved_pos = this.current;
13787
- const frame_name_token = this.advance();
13788
- this.skipTrivia();
13789
- if (this.check("COLON")) {
13790
- this.advance();
13791
- const frame_prefix = {
13792
- type: "prefix",
13793
- name: "frame",
13794
- fullName: "frame",
13795
- frameName: frame_name_token.value,
13796
- has_colon: true,
13797
- range: this.makeRange(command_token.range.start, this.previous().range.end)
13798
- };
13799
- return this.parseFramePrefixedCommand(frame_prefix, prefixes, start_token);
13800
- } else {
13801
- this.current = saved_pos;
13893
+ if (this.check("WORD")) {
13894
+ const frame_name_token = this.advance();
13895
+ this.skipTrivia();
13896
+ if (this.check("COLON")) {
13897
+ this.advance();
13898
+ const frame_prefix = {
13899
+ type: "prefix",
13900
+ name: "frame",
13901
+ fullName: "frame",
13902
+ frameName: frame_name_token.value,
13903
+ has_colon: true,
13904
+ range: this.makeRange(command_token.range.start, this.previous().range.end)
13905
+ };
13906
+ return this.parseFramePrefixedCommand(frame_prefix, prefixes, start_token);
13907
+ }
13802
13908
  }
13909
+ this.current = saved_pos;
13803
13910
  }
13804
13911
  return this.parseCommandBody(command_token, prefixes, start_token);
13805
13912
  }
@@ -13917,6 +14024,9 @@ var init_parser = __esm(() => {
13917
14024
  const parsed = this.parse_option_argument_inside_parens();
13918
14025
  option.argument = parsed.argument;
13919
14026
  option.argument_range = parsed.argument_range;
14027
+ if (parsed.argument_unclosed) {
14028
+ option.argument_unclosed = true;
14029
+ }
13920
14030
  }
13921
14031
  options.push(option);
13922
14032
  } else {
@@ -13939,19 +14049,41 @@ var init_parser = __esm(() => {
13939
14049
  }
13940
14050
  parse_option_argument_inside_parens() {
13941
14051
  this.advance();
13942
- let argument = "";
13943
14052
  const arg_tokens = [];
13944
- while (!this.check("RPAREN") && !this.isAtEnd()) {
14053
+ const preceded_by_continuation = [];
14054
+ const continuation = new ContinuationTracker;
14055
+ let paren_depth = 1;
14056
+ while (!this.isAtEnd() && paren_depth > 0) {
14057
+ if (this.check("STATEMENT_TERMINATOR")) {
14058
+ break;
14059
+ }
14060
+ if (this.check("CONTINUATION")) {
14061
+ const cont_token = this.peek();
14062
+ if (this.skipContinuation()) {
14063
+ continuation.note_continuation(cont_token);
14064
+ continue;
14065
+ }
14066
+ }
14067
+ if (this.check("COMMENT_LINE") || this.check("COMMENT_BLOCK")) {
14068
+ this.advance();
14069
+ continue;
14070
+ }
13945
14071
  const t = this.advance();
14072
+ if (t.type === "LPAREN") {
14073
+ paren_depth++;
14074
+ } else if (t.type === "RPAREN") {
14075
+ paren_depth--;
14076
+ if (paren_depth === 0) {
14077
+ break;
14078
+ }
14079
+ }
13946
14080
  arg_tokens.push(t);
13947
- argument += t.value;
14081
+ preceded_by_continuation.push(continuation.collect(t));
13948
14082
  }
13949
- if (this.check("RPAREN")) {
13950
- this.advance();
13951
- }
13952
- const non_ws = arg_tokens.filter((t) => t.type !== "WHITESPACE");
13953
- const argument_range = non_ws.length > 0 ? { start: non_ws[0].range.start, end: non_ws[non_ws.length - 1].range.end } : undefined;
13954
- return { argument, argument_range };
14083
+ const argument_unclosed = paren_depth > 0 ? true : undefined;
14084
+ const argument = this.reconstruct_value_tokens(arg_tokens, preceded_by_continuation);
14085
+ const argument_range = arg_tokens.length > 0 ? { start: arg_tokens[0].range.start, end: arg_tokens[arg_tokens.length - 1].range.end } : undefined;
14086
+ return { argument, argument_range, argument_unclosed };
13955
14087
  }
13956
14088
  parseFilePathArgument() {
13957
14089
  if (this.check("STRING")) {
@@ -13967,13 +14099,18 @@ var init_parser = __esm(() => {
13967
14099
  const start_token = this.advance();
13968
14100
  let path3 = start_token.value;
13969
14101
  let end_range = start_token.range.end;
14102
+ let prev_token = start_token;
13970
14103
  while (!this.isAtEnd()) {
13971
- if (this.check("WHITESPACE") || this.check("COMMA") || this.check("STATEMENT_TERMINATOR") || this.isTrivia()) {
14104
+ if (this.check("COMMA") || this.check("STATEMENT_TERMINATOR") || this.isTrivia()) {
14105
+ break;
14106
+ }
14107
+ if (!this.isAdjacentToken(prev_token, this.peek())) {
13972
14108
  break;
13973
14109
  }
13974
14110
  const token = this.advance();
13975
14111
  path3 += token.value;
13976
14112
  end_range = token.range.end;
14113
+ prev_token = token;
13977
14114
  }
13978
14115
  return {
13979
14116
  name: path3,
@@ -13982,39 +14119,56 @@ var init_parser = __esm(() => {
13982
14119
  }
13983
14120
  parseParenthesizedGroup() {
13984
14121
  const paren_start = this.advance();
13985
- const paren_parts = [];
14122
+ const inner_tokens = [];
14123
+ const preceded_by_continuation = [];
14124
+ const continuation = new ContinuationTracker;
13986
14125
  let paren_depth = 1;
13987
- let last_was_word = false;
14126
+ const collect = (my_token) => {
14127
+ inner_tokens.push(my_token);
14128
+ preceded_by_continuation.push(continuation.collect(my_token));
14129
+ };
13988
14130
  while (!this.isAtEnd() && paren_depth > 0) {
14131
+ if (this.check("STATEMENT_TERMINATOR")) {
14132
+ break;
14133
+ }
14134
+ if (this.check("CONTINUATION")) {
14135
+ const cont_token = this.peek();
14136
+ if (this.skipContinuation()) {
14137
+ continuation.note_continuation(cont_token);
14138
+ continue;
14139
+ }
14140
+ }
13989
14141
  if (this.check("LPAREN")) {
13990
14142
  paren_depth++;
13991
- paren_parts.push(this.advance().value);
13992
- last_was_word = false;
14143
+ collect(this.advance());
13993
14144
  } else if (this.check("RPAREN")) {
13994
14145
  paren_depth--;
13995
14146
  if (paren_depth > 0) {
13996
- paren_parts.push(this.advance().value);
14147
+ collect(this.advance());
13997
14148
  }
13998
- last_was_word = false;
13999
14149
  } else {
14000
- const current_is_word = this.check("WORD") || this.check("NUMBER") || this.check("MACRO_REF_LOCAL") || this.check("MACRO_REF_GLOBAL");
14001
- if (last_was_word && current_is_word) {
14002
- paren_parts.push(" ");
14003
- }
14004
- paren_parts.push(this.advance().value);
14005
- last_was_word = current_is_word;
14150
+ collect(this.advance());
14006
14151
  }
14007
14152
  }
14008
- const paren_content = paren_parts.join("");
14009
- const paren_end_pos = this.check("RPAREN") ? this.peek().range.end : this.previous().range.end;
14010
- if (this.check("RPAREN")) {
14153
+ const paren_content = this.reconstruct_value_tokens(inner_tokens, preceded_by_continuation);
14154
+ const closed_group = paren_depth === 0 && this.check("RPAREN");
14155
+ const paren_end_pos = closed_group ? this.peek().range.end : this.previous().range.end;
14156
+ if (closed_group) {
14011
14157
  this.advance();
14012
14158
  }
14013
14159
  if (!paren_content.trim()) {
14014
- return null;
14160
+ if (closed_group) {
14161
+ return null;
14162
+ }
14163
+ return {
14164
+ name: "(",
14165
+ recovery_only: true,
14166
+ range: this.makeRange(paren_start.range.start, paren_end_pos)
14167
+ };
14015
14168
  }
14169
+ const group_name = closed_group ? `(${paren_content})` : `(${paren_content}`;
14016
14170
  return {
14017
- name: `(${paren_content})`,
14171
+ name: group_name,
14018
14172
  range: this.makeRange(paren_start.range.start, paren_end_pos)
14019
14173
  };
14020
14174
  }
@@ -14071,6 +14225,9 @@ var init_parser = __esm(() => {
14071
14225
  const parsed = this.parse_option_argument_inside_parens();
14072
14226
  option.argument = parsed.argument;
14073
14227
  option.argument_range = parsed.argument_range;
14228
+ if (parsed.argument_unclosed) {
14229
+ option.argument_unclosed = true;
14230
+ }
14074
14231
  }
14075
14232
  options.push(option);
14076
14233
  } else {
@@ -14123,8 +14280,22 @@ var init_parser = __esm(() => {
14123
14280
  let allows_arbitrary_options = false;
14124
14281
  const seen_option_names = new Set;
14125
14282
  const syntax_tokens = [];
14126
- while (!this.check("STATEMENT_TERMINATOR") && !this.isAtEnd() && !this.isTrivia()) {
14127
- syntax_tokens.push(this.advance());
14283
+ const preceded_by_continuation = [];
14284
+ const continuation = new ContinuationTracker;
14285
+ while (!this.check("STATEMENT_TERMINATOR") && !this.isAtEnd()) {
14286
+ if (this.check("CONTINUATION")) {
14287
+ const cont_token = this.peek();
14288
+ if (this.skipContinuation()) {
14289
+ continuation.note_continuation(cont_token);
14290
+ continue;
14291
+ }
14292
+ }
14293
+ if (this.check("COMMENT_LINE") || this.check("COMMENT_BLOCK")) {
14294
+ break;
14295
+ }
14296
+ const my_token = this.advance();
14297
+ syntax_tokens.push(my_token);
14298
+ preceded_by_continuation.push(continuation.collect(my_token));
14128
14299
  }
14129
14300
  let token_idx = 0;
14130
14301
  while (token_idx < syntax_tokens.length) {
@@ -14145,7 +14316,7 @@ var init_parser = __esm(() => {
14145
14316
  const my_token = syntax_tokens[token_idx];
14146
14317
  if (my_token.type === "OPERATOR" && my_token.value === "*") {
14147
14318
  if (token_idx + 1 < syntax_tokens.length && syntax_tokens[token_idx + 1].type === "WORD") {
14148
- const opt_result2 = this.parse_option_spec(syntax_tokens, token_idx);
14319
+ const opt_result2 = this.parse_option_spec(syntax_tokens, token_idx, preceded_by_continuation);
14149
14320
  if (opt_result2) {
14150
14321
  const opt_name_lower = opt_result2.spec.name;
14151
14322
  seen_option_names.add(opt_name_lower);
@@ -14160,7 +14331,7 @@ var init_parser = __esm(() => {
14160
14331
  }
14161
14332
  continue;
14162
14333
  }
14163
- const opt_result = this.parse_option_spec(syntax_tokens, token_idx);
14334
+ const opt_result = this.parse_option_spec(syntax_tokens, token_idx, preceded_by_continuation);
14164
14335
  if (opt_result) {
14165
14336
  const opt_name_lower = opt_result.spec.name;
14166
14337
  seen_option_names.add(opt_name_lower);
@@ -14269,7 +14440,7 @@ var init_parser = __esm(() => {
14269
14440
  };
14270
14441
  return { spec, next_idx: current_idx };
14271
14442
  }
14272
- parse_option_spec(tokens, start_idx) {
14443
+ parse_option_spec(tokens, start_idx, preceded_by_continuation = []) {
14273
14444
  if (start_idx >= tokens.length) {
14274
14445
  return null;
14275
14446
  }
@@ -14302,6 +14473,7 @@ var init_parser = __esm(() => {
14302
14473
  if (current_idx < tokens.length && tokens[current_idx].type === "LPAREN") {
14303
14474
  current_idx++;
14304
14475
  const type_tokens = [];
14476
+ const type_flags = [];
14305
14477
  let paren_depth = 1;
14306
14478
  while (current_idx < tokens.length && paren_depth > 0) {
14307
14479
  const my_token = tokens[current_idx];
@@ -14314,6 +14486,7 @@ var init_parser = __esm(() => {
14314
14486
  }
14315
14487
  }
14316
14488
  type_tokens.push(my_token);
14489
+ type_flags.push(preceded_by_continuation[current_idx] ?? false);
14317
14490
  current_idx++;
14318
14491
  }
14319
14492
  if (current_idx < tokens.length && tokens[current_idx].type === "RPAREN") {
@@ -14336,25 +14509,25 @@ var init_parser = __esm(() => {
14336
14509
  argument_type = first_part;
14337
14510
  for (let i = 1;i < type_tokens.length; i++) {
14338
14511
  if (type_tokens[i].type === "WORD" && type_tokens[i].value === "default" && i + 1 < type_tokens.length && type_tokens[i + 1].type === "LPAREN") {
14339
- let default_str = "";
14512
+ const default_tokens = [];
14513
+ const default_flags = [];
14340
14514
  let default_paren_depth = 1;
14341
14515
  let j = i + 2;
14342
14516
  while (j < type_tokens.length && default_paren_depth > 0) {
14343
14517
  const my_token = type_tokens[j];
14344
14518
  if (my_token.type === "LPAREN") {
14345
14519
  default_paren_depth++;
14346
- default_str += my_token.value;
14347
14520
  } else if (my_token.type === "RPAREN") {
14348
14521
  default_paren_depth--;
14349
- if (default_paren_depth > 0) {
14350
- default_str += my_token.value;
14522
+ if (default_paren_depth === 0) {
14523
+ break;
14351
14524
  }
14352
- } else {
14353
- default_str += my_token.value;
14354
14525
  }
14526
+ default_tokens.push(my_token);
14527
+ default_flags.push(type_flags[j] ?? false);
14355
14528
  j++;
14356
14529
  }
14357
- default_value = default_str.trim();
14530
+ default_value = this.reconstruct_value_tokens(default_tokens, default_flags).trim();
14358
14531
  break;
14359
14532
  }
14360
14533
  }
@@ -14455,11 +14628,17 @@ var init_parser = __esm(() => {
14455
14628
  parseIfStatement() {
14456
14629
  const ifToken = this.advance();
14457
14630
  const condition_tokens = [];
14631
+ const preceded_by_continuation = [];
14632
+ const continuation = new ContinuationTracker;
14458
14633
  const condition_start_line = ifToken.range.start.line;
14459
14634
  let paren_depth = 0;
14460
14635
  while (!this.check("LBRACE") && !this.isAtEnd()) {
14461
- if (this.skipContinuation()) {
14462
- continue;
14636
+ if (this.check("CONTINUATION")) {
14637
+ const cont_token = this.peek();
14638
+ if (this.skipContinuation()) {
14639
+ continuation.note_continuation(cont_token);
14640
+ continue;
14641
+ }
14463
14642
  }
14464
14643
  if (this.check("STATEMENT_TERMINATOR")) {
14465
14644
  break;
@@ -14474,11 +14653,10 @@ var init_parser = __esm(() => {
14474
14653
  paren_depth = 0;
14475
14654
  }
14476
14655
  }
14477
- if (token.type !== "WHITESPACE") {
14478
- condition_tokens.push(token);
14479
- }
14656
+ condition_tokens.push(token);
14657
+ preceded_by_continuation.push(continuation.collect(token));
14480
14658
  }
14481
- const condition = this.reconstructTokensWithSpacing(condition_tokens).trim();
14659
+ const condition = this.reconstruct_value_tokens(condition_tokens, preceded_by_continuation).trim();
14482
14660
  if (paren_depth > 0) {
14483
14661
  this.addError("Unbalanced parentheses in if condition: missing closing parenthesis", ifToken.range, "UNBALANCED_PARENTHESES" /* UNBALANCED_PARENTHESES */);
14484
14662
  }
@@ -14486,17 +14664,15 @@ var init_parser = __esm(() => {
14486
14664
  this.addError("Empty if condition", ifToken.range, "MISSING_EXPRESSION_AFTER_EQUALS" /* MISSING_EXPRESSION_AFTER_EQUALS */);
14487
14665
  }
14488
14666
  const body = [];
14667
+ let blockEndingTrivia;
14489
14668
  if (this.check("LBRACE")) {
14490
14669
  const lbrace_index = this.current;
14491
14670
  const lbrace_token = this.advance();
14492
14671
  const has_condition_before = lbrace_token.range.start.line === condition_start_line;
14493
14672
  this.validate_open_brace(lbrace_token, lbrace_index, has_condition_before);
14494
- while (!this.check("RBRACE") && !this.isAtEnd()) {
14495
- const stmt = this.parseStatement();
14496
- if (stmt) {
14497
- body.push(stmt);
14498
- }
14499
- }
14673
+ const { body: my_body, blockEndingTrivia: my_block_ending_trivia } = this.parseBraceBody();
14674
+ body.push(...my_body);
14675
+ blockEndingTrivia = my_block_ending_trivia;
14500
14676
  if (this.check("RBRACE")) {
14501
14677
  const rbrace_index = this.current;
14502
14678
  const rbrace_token = this.advance();
@@ -14509,7 +14685,8 @@ var init_parser = __esm(() => {
14509
14685
  type: "if",
14510
14686
  condition: condition.trim(),
14511
14687
  body,
14512
- range: this.makeRange(ifToken.range.start, this.previous().range.end)
14688
+ range: this.makeRange(ifToken.range.start, this.previous().range.end),
14689
+ blockEndingTrivia
14513
14690
  };
14514
14691
  }
14515
14692
  parseElseStatement() {
@@ -14523,17 +14700,17 @@ var init_parser = __esm(() => {
14523
14700
  };
14524
14701
  }
14525
14702
  const body = [];
14703
+ let blockEndingTrivia;
14704
+ let is_brace_else = false;
14526
14705
  if (this.check("LBRACE")) {
14706
+ is_brace_else = true;
14527
14707
  const lbrace_index = this.current;
14528
14708
  const lbrace_token = this.advance();
14529
14709
  const has_condition_before = lbrace_token.range.start.line === elseToken.range.start.line;
14530
14710
  this.validate_open_brace(lbrace_token, lbrace_index, has_condition_before);
14531
- while (!this.check("RBRACE") && !this.isAtEnd()) {
14532
- const stmt = this.parseStatement();
14533
- if (stmt) {
14534
- body.push(stmt);
14535
- }
14536
- }
14711
+ const { body: my_body, blockEndingTrivia: my_block_ending_trivia } = this.parseBraceBody();
14712
+ body.push(...my_body);
14713
+ blockEndingTrivia = my_block_ending_trivia;
14537
14714
  if (this.check("RBRACE")) {
14538
14715
  const rbrace_index = this.current;
14539
14716
  const rbrace_token = this.advance();
@@ -14547,10 +14724,12 @@ var init_parser = __esm(() => {
14547
14724
  body.push(stmt);
14548
14725
  }
14549
14726
  }
14727
+ const end_position = !is_brace_else && body.length > 0 ? body[body.length - 1].range.end : this.previous().range.end;
14550
14728
  return {
14551
14729
  type: "else",
14552
14730
  body,
14553
- range: this.makeRange(elseToken.range.start, this.previous().range.end)
14731
+ range: this.makeRange(elseToken.range.start, end_position),
14732
+ blockEndingTrivia
14554
14733
  };
14555
14734
  }
14556
14735
  parseLoopStatement() {
@@ -14566,9 +14745,9 @@ var init_parser = __esm(() => {
14566
14745
  const is_foreach_spec = this.check("WORD") && (this.peek().value === "in" || this.peek().value === "of");
14567
14746
  if (is_forvalues_spec || is_foreach_spec) {
14568
14747
  const specType = this.advance().value;
14569
- let loop_spec_body = "";
14570
- let prev_spec_token = null;
14571
- let continuation_pending = false;
14748
+ const spec_tokens = [];
14749
+ const preceded_by_continuation = [];
14750
+ const continuation = new ContinuationTracker;
14572
14751
  while (!this.check("LBRACE") && !this.isAtEnd()) {
14573
14752
  if (this.check("STATEMENT_TERMINATOR")) {
14574
14753
  if (is_swallowed_continuation_terminator(this.tokens[this.current], this.current > 0 && this.tokens[this.current - 1].type === "CONTINUATION")) {
@@ -14579,44 +14758,24 @@ var init_parser = __esm(() => {
14579
14758
  }
14580
14759
  const token = this.advance();
14581
14760
  if (token.type === "CONTINUATION") {
14582
- continuation_pending = true;
14583
- continue;
14584
- }
14585
- if (token.type === "WHITESPACE") {
14761
+ continuation.note_continuation(token);
14586
14762
  continue;
14587
14763
  }
14588
- if (prev_spec_token !== null) {
14589
- const same_line = prev_spec_token.range.end.line === token.range.start.line;
14590
- let needs_separator;
14591
- if (same_line) {
14592
- needs_separator = token.range.start.character - prev_spec_token.range.end.character > 0;
14593
- } else if (continuation_pending) {
14594
- needs_separator = token.range.start.character > 0;
14595
- } else {
14596
- needs_separator = true;
14597
- }
14598
- if (needs_separator) {
14599
- loop_spec_body += " ";
14600
- }
14601
- }
14602
- loop_spec_body += token.value;
14603
- prev_spec_token = token;
14604
- continuation_pending = false;
14764
+ spec_tokens.push(token);
14765
+ preceded_by_continuation.push(continuation.collect(token));
14605
14766
  }
14606
- loopSpec = specType + " " + loop_spec_body.trim();
14767
+ loopSpec = specType + " " + this.reconstruct_value_tokens(spec_tokens, preceded_by_continuation).trim();
14607
14768
  }
14608
14769
  const body = [];
14770
+ let blockEndingTrivia;
14609
14771
  if (this.check("LBRACE")) {
14610
14772
  const lbrace_index = this.current;
14611
14773
  const lbrace_token = this.advance();
14612
14774
  const has_condition_before = lbrace_token.range.start.line === loop_start_line;
14613
14775
  this.validate_open_brace(lbrace_token, lbrace_index, has_condition_before);
14614
- while (!this.check("RBRACE") && !this.isAtEnd()) {
14615
- const stmt = this.parseStatement();
14616
- if (stmt) {
14617
- body.push(stmt);
14618
- }
14619
- }
14776
+ const { body: my_body, blockEndingTrivia: my_block_ending_trivia } = this.parseBraceBody();
14777
+ body.push(...my_body);
14778
+ blockEndingTrivia = my_block_ending_trivia;
14620
14779
  if (this.check("RBRACE")) {
14621
14780
  const rbrace_index = this.current;
14622
14781
  const rbrace_token = this.advance();
@@ -14630,17 +14789,24 @@ var init_parser = __esm(() => {
14630
14789
  loopVar,
14631
14790
  loopSpec,
14632
14791
  body,
14633
- range: this.makeRange(loopToken.range.start, this.previous().range.end)
14792
+ range: this.makeRange(loopToken.range.start, this.previous().range.end),
14793
+ blockEndingTrivia
14634
14794
  };
14635
14795
  }
14636
14796
  parseWhileStatement() {
14637
14797
  const whileToken = this.advance();
14638
14798
  const while_start_line = whileToken.range.start.line;
14639
14799
  const condition_tokens = [];
14800
+ const preceded_by_continuation = [];
14801
+ const continuation = new ContinuationTracker;
14640
14802
  let paren_depth = 0;
14641
14803
  while (!this.check("LBRACE") && !this.isAtEnd()) {
14642
- if (this.skipContinuation()) {
14643
- continue;
14804
+ if (this.check("CONTINUATION")) {
14805
+ const cont_token = this.peek();
14806
+ if (this.skipContinuation()) {
14807
+ continuation.note_continuation(cont_token);
14808
+ continue;
14809
+ }
14644
14810
  }
14645
14811
  if (this.check("STATEMENT_TERMINATOR")) {
14646
14812
  break;
@@ -14655,11 +14821,10 @@ var init_parser = __esm(() => {
14655
14821
  paren_depth = 0;
14656
14822
  }
14657
14823
  }
14658
- if (token.type !== "WHITESPACE") {
14659
- condition_tokens.push(token);
14660
- }
14824
+ condition_tokens.push(token);
14825
+ preceded_by_continuation.push(continuation.collect(token));
14661
14826
  }
14662
- const condition = this.reconstructTokensWithSpacing(condition_tokens).trim();
14827
+ const condition = this.reconstruct_value_tokens(condition_tokens, preceded_by_continuation).trim();
14663
14828
  if (paren_depth > 0) {
14664
14829
  this.addError("Unbalanced parentheses in while condition: missing closing parenthesis", whileToken.range, "UNBALANCED_PARENTHESES" /* UNBALANCED_PARENTHESES */);
14665
14830
  }
@@ -14667,17 +14832,15 @@ var init_parser = __esm(() => {
14667
14832
  this.addError("Empty while condition", whileToken.range, "MISSING_EXPRESSION_AFTER_EQUALS" /* MISSING_EXPRESSION_AFTER_EQUALS */);
14668
14833
  }
14669
14834
  const body = [];
14835
+ let blockEndingTrivia;
14670
14836
  if (this.check("LBRACE")) {
14671
14837
  const lbrace_index = this.current;
14672
14838
  const lbrace_token = this.advance();
14673
14839
  const has_condition_before = lbrace_token.range.start.line === while_start_line;
14674
14840
  this.validate_open_brace(lbrace_token, lbrace_index, has_condition_before);
14675
- while (!this.check("RBRACE") && !this.isAtEnd()) {
14676
- const stmt = this.parseStatement();
14677
- if (stmt) {
14678
- body.push(stmt);
14679
- }
14680
- }
14841
+ const { body: my_body, blockEndingTrivia: my_block_ending_trivia } = this.parseBraceBody();
14842
+ body.push(...my_body);
14843
+ blockEndingTrivia = my_block_ending_trivia;
14681
14844
  if (this.check("RBRACE")) {
14682
14845
  const rbrace_index = this.current;
14683
14846
  const rbrace_token = this.advance();
@@ -14690,7 +14853,8 @@ var init_parser = __esm(() => {
14690
14853
  type: "while",
14691
14854
  condition: condition.trim(),
14692
14855
  body,
14693
- range: this.makeRange(whileToken.range.start, this.previous().range.end)
14856
+ range: this.makeRange(whileToken.range.start, this.previous().range.end),
14857
+ blockEndingTrivia
14694
14858
  };
14695
14859
  }
14696
14860
  parseStringStatement() {
@@ -14698,9 +14862,7 @@ var init_parser = __esm(() => {
14698
14862
  const statement_tokens = [];
14699
14863
  while (!this.check("STATEMENT_TERMINATOR") && !this.isAtEnd()) {
14700
14864
  const token = this.advance();
14701
- if (token.type !== "WHITESPACE") {
14702
- statement_tokens.push(token);
14703
- }
14865
+ statement_tokens.push(token);
14704
14866
  }
14705
14867
  const content = this.reconstructTokensWithSpacing(statement_tokens);
14706
14868
  return {
@@ -14711,15 +14873,15 @@ var init_parser = __esm(() => {
14711
14873
  };
14712
14874
  }
14713
14875
  parseFrameBlock() {
14876
+ const frame_index = this.current;
14714
14877
  const frame_token = this.advance();
14715
14878
  const frame_start_line = frame_token.range.start.line;
14716
14879
  this.skipTrivia();
14717
14880
  if (!this.check("WORD")) {
14718
- this.current--;
14881
+ this.current = frame_index;
14719
14882
  return null;
14720
14883
  }
14721
14884
  const name_token = this.peek();
14722
- const saved_position = this.current;
14723
14885
  this.advance();
14724
14886
  this.skipTrivia();
14725
14887
  if (this.check("COLON")) {
@@ -14735,7 +14897,7 @@ var init_parser = __esm(() => {
14735
14897
  return this.parseFramePrefixedCommand(frame_prefix, [], frame_token);
14736
14898
  }
14737
14899
  if (!this.check("LBRACE")) {
14738
- this.current = saved_position - 1;
14900
+ this.current = frame_index;
14739
14901
  return null;
14740
14902
  }
14741
14903
  const frame_name = name_token.value;
@@ -14743,13 +14905,7 @@ var init_parser = __esm(() => {
14743
14905
  const lbrace_token = this.advance();
14744
14906
  const has_condition_before = lbrace_token.range.start.line === frame_start_line;
14745
14907
  this.validate_open_brace(lbrace_token, lbrace_index, has_condition_before);
14746
- const body = [];
14747
- while (!this.check("RBRACE") && !this.isAtEnd()) {
14748
- const stmt = this.parseStatement();
14749
- if (stmt) {
14750
- body.push(stmt);
14751
- }
14752
- }
14908
+ const { body, blockEndingTrivia } = this.parseBraceBody();
14753
14909
  if (this.check("RBRACE")) {
14754
14910
  const rbrace_index = this.current;
14755
14911
  const rbrace_token = this.advance();
@@ -14761,7 +14917,8 @@ var init_parser = __esm(() => {
14761
14917
  type: "frame",
14762
14918
  frameName: frame_name,
14763
14919
  body,
14764
- range: this.makeRange(frame_token.range.start, this.previous().range.end)
14920
+ range: this.makeRange(frame_token.range.start, this.previous().range.end),
14921
+ blockEndingTrivia
14765
14922
  };
14766
14923
  }
14767
14924
  parseUnabCommandBody(command_token) {
@@ -14822,6 +14979,9 @@ var init_parser = __esm(() => {
14822
14979
  const parsed = this.parse_option_argument_inside_parens();
14823
14980
  option.argument = parsed.argument;
14824
14981
  option.argument_range = parsed.argument_range;
14982
+ if (parsed.argument_unclosed) {
14983
+ option.argument_unclosed = true;
14984
+ }
14825
14985
  }
14826
14986
  options.push(option);
14827
14987
  } else {
@@ -14848,7 +15008,7 @@ var init_parser = __esm(() => {
14848
15008
  }
14849
15009
  collectTrivia() {
14850
15010
  const trivia = [];
14851
- while (this.check("COMMENT_LINE") || this.check("COMMENT_BLOCK") || this.check("CONTINUATION") || this.check("WHITESPACE")) {
15011
+ while (this.check("COMMENT_LINE") || this.check("COMMENT_BLOCK") || this.check("CONTINUATION")) {
14852
15012
  const token = this.advance();
14853
15013
  if (token.type === "COMMENT_LINE") {
14854
15014
  const style = token.value.startsWith("*") ? "star" : "slash";
@@ -14881,12 +15041,42 @@ var init_parser = __esm(() => {
14881
15041
  this.advance();
14882
15042
  }
14883
15043
  }
15044
+ parseBraceBody() {
15045
+ const body = [];
15046
+ while (!this.isAtEnd()) {
15047
+ const my_trivia = this.collectTrivia();
15048
+ if (my_trivia.length > 0) {
15049
+ this.pending_trivia.push(...my_trivia);
15050
+ }
15051
+ if (this.check("RBRACE") || this.isAtEnd()) {
15052
+ break;
15053
+ }
15054
+ const stmt = this.parseStatement();
15055
+ if (stmt) {
15056
+ body.push(stmt);
15057
+ }
15058
+ }
15059
+ const blockEndingTrivia = this.check("RBRACE") ? this.drainBlockEndingTrivia() : undefined;
15060
+ return { body, blockEndingTrivia };
15061
+ }
15062
+ drainBlockEndingTrivia() {
15063
+ if (this.pending_trivia.length === 0) {
15064
+ return;
15065
+ }
15066
+ const has_continuation = this.pending_trivia.some((my_trivia) => my_trivia.type === "comment" && my_trivia.style === "continuation");
15067
+ if (has_continuation) {
15068
+ return;
15069
+ }
15070
+ const block_ending_trivia = this.pending_trivia;
15071
+ this.pending_trivia = [];
15072
+ return block_ending_trivia;
15073
+ }
14884
15074
  skipMacroDefinitionTrivia() {
14885
15075
  while (!this.isAtEnd()) {
14886
15076
  if (this.skipContinuation()) {
14887
15077
  continue;
14888
15078
  }
14889
- if (this.check("WHITESPACE") || this.check("COMMENT_LINE") || this.check("COMMENT_BLOCK")) {
15079
+ if (this.check("COMMENT_LINE") || this.check("COMMENT_BLOCK")) {
14890
15080
  this.advance();
14891
15081
  continue;
14892
15082
  }
@@ -14903,7 +15093,7 @@ var init_parser = __esm(() => {
14903
15093
  }
14904
15094
  continue;
14905
15095
  }
14906
- if (token.type === "COMMENT_LINE" || token.type === "COMMENT_BLOCK" || token.type === "WHITESPACE") {
15096
+ if (token.type === "COMMENT_LINE" || token.type === "COMMENT_BLOCK") {
14907
15097
  offset++;
14908
15098
  continue;
14909
15099
  }
@@ -14985,11 +15175,6 @@ var init_parser = __esm(() => {
14985
15175
  peek() {
14986
15176
  return this.tokens[this.current];
14987
15177
  }
14988
- peekNext() {
14989
- if (this.current + 1 >= this.tokens.length)
14990
- return;
14991
- return this.tokens[this.current + 1];
14992
- }
14993
15178
  previous() {
14994
15179
  return this.tokens[this.current - 1];
14995
15180
  }
@@ -15007,12 +15192,18 @@ var init_parser = __esm(() => {
15007
15192
  return this.context_tracker;
15008
15193
  }
15009
15194
  parseExpression() {
15010
- let expression = "";
15195
+ const the_tokens = [];
15196
+ const preceded_by_continuation = [];
15197
+ const continuation = new ContinuationTracker;
15011
15198
  let paren_depth = 0;
15012
15199
  const start_pos = this.current;
15013
15200
  while (!this.isAtEnd()) {
15014
15201
  const token = this.peek();
15202
+ if (token.type === "STATEMENT_TERMINATOR") {
15203
+ break;
15204
+ }
15015
15205
  if (this.skipContinuation()) {
15206
+ continuation.note_continuation(token);
15016
15207
  continue;
15017
15208
  }
15018
15209
  if (token.type === "LPAREN") {
@@ -15025,7 +15216,7 @@ var init_parser = __esm(() => {
15025
15216
  }
15026
15217
  }
15027
15218
  if (paren_depth === 0) {
15028
- if (token.type === "COMMA" || token.type === "STATEMENT_TERMINATOR") {
15219
+ if (token.type === "COMMA") {
15029
15220
  break;
15030
15221
  }
15031
15222
  if (token.type === "WORD" && (token.value === "if" || token.value === "in")) {
@@ -15035,13 +15226,11 @@ var init_parser = __esm(() => {
15035
15226
  if (token.type === "COMMENT_LINE" || token.type === "COMMENT_BLOCK") {
15036
15227
  break;
15037
15228
  }
15038
- const tokenValue = this.advance().value;
15039
- if (token.type === "WHITESPACE") {
15040
- expression += " ";
15041
- } else {
15042
- expression += tokenValue;
15043
- }
15229
+ this.advance();
15230
+ the_tokens.push(token);
15231
+ preceded_by_continuation.push(continuation.collect(token));
15044
15232
  }
15233
+ const expression = this.reconstruct_value_tokens(the_tokens, preceded_by_continuation);
15045
15234
  if (paren_depth > 0) {
15046
15235
  const end_pos = this.current > 0 ? this.previous().range : this.peek().range;
15047
15236
  this.addError("Unbalanced parentheses: missing closing parenthesis", end_pos, "UNBALANCED_PARENTHESES" /* UNBALANCED_PARENTHESES */);
@@ -15073,7 +15262,9 @@ var init_parser = __esm(() => {
15073
15262
  return false;
15074
15263
  }
15075
15264
  parseQualifierExpressionWithStrayDetection(qualifier_type, stop_at_in, check_empty) {
15076
- let expression = "";
15265
+ const the_tokens = [];
15266
+ const preceded_by_continuation = [];
15267
+ const continuation = new ContinuationTracker;
15077
15268
  let paren_depth = 0;
15078
15269
  let bracket_depth = 0;
15079
15270
  const start_token = this.peek();
@@ -15082,6 +15273,9 @@ var init_parser = __esm(() => {
15082
15273
  let in_string_context = false;
15083
15274
  while (!this.isAtEnd()) {
15084
15275
  const token = this.peek();
15276
+ if (token.type === "STATEMENT_TERMINATOR") {
15277
+ break;
15278
+ }
15085
15279
  if (token.type === "LPAREN") {
15086
15280
  paren_depth++;
15087
15281
  state_stack.push("INITIAL");
@@ -15112,10 +15306,11 @@ var init_parser = __esm(() => {
15112
15306
  }
15113
15307
  }
15114
15308
  if (this.skipContinuation()) {
15309
+ continuation.note_continuation(token);
15115
15310
  continue;
15116
15311
  }
15117
15312
  if (paren_depth === 0 && bracket_depth === 0) {
15118
- if (token.type === "STATEMENT_TERMINATOR" || token.type === "COMMA") {
15313
+ if (token.type === "COMMA") {
15119
15314
  break;
15120
15315
  }
15121
15316
  if (stop_at_in && token.type === "WORD" && token.value === "in") {
@@ -15138,7 +15333,7 @@ var init_parser = __esm(() => {
15138
15333
  this.addError(`Unexpected token '${token.value}' after comparison expression. Did you mean to use '&' or '|'?`, token.range, "STRAY_TOKEN_IN_CONDITION" /* STRAY_TOKEN_IN_CONDITION */);
15139
15334
  }
15140
15335
  }
15141
- if (token.type !== "WHITESPACE" && token.type !== "LPAREN" && token.type !== "RPAREN" && bracket_depth === 0) {
15336
+ if (token.type !== "LPAREN" && token.type !== "RPAREN" && bracket_depth === 0) {
15142
15337
  const current_state_for_transition = state_stack[state_stack.length - 1];
15143
15338
  if (token.type === "OPERATOR") {
15144
15339
  if (token.value === "=" && prev_token && prev_token.type === "OPERATOR" && prev_token.value === "~") {
@@ -15166,17 +15361,14 @@ var init_parser = __esm(() => {
15166
15361
  }
15167
15362
  prev_token = token;
15168
15363
  }
15169
- const tokenValue = this.advance().value;
15170
- if (token.type === "WHITESPACE") {
15171
- expression += " ";
15172
- } else {
15173
- expression += tokenValue;
15174
- }
15364
+ this.advance();
15365
+ the_tokens.push(token);
15366
+ preceded_by_continuation.push(continuation.collect(token));
15175
15367
  }
15176
15368
  if (paren_depth > 0) {
15177
15369
  this.addError(`Unbalanced parentheses in ${qualifier_type} qualifier: missing closing parenthesis`, start_token.range, "UNBALANCED_PARENTHESES" /* UNBALANCED_PARENTHESES */);
15178
15370
  }
15179
- const trimmed_expression = expression.trim();
15371
+ const trimmed_expression = this.reconstruct_value_tokens(the_tokens, preceded_by_continuation).trim();
15180
15372
  if (check_empty && trimmed_expression === "") {
15181
15373
  this.addError(`Empty ${qualifier_type} qualifier expression`, start_token.range, "MISSING_EXPRESSION_AFTER_EQUALS" /* MISSING_EXPRESSION_AFTER_EQUALS */);
15182
15374
  }
@@ -15219,7 +15411,6 @@ var init_parser = __esm(() => {
15219
15411
  }
15220
15412
  scan_code_on_same_line(start_pos, line) {
15221
15413
  const trivia_types = [
15222
- "WHITESPACE",
15223
15414
  "COMMENT_LINE",
15224
15415
  "COMMENT_BLOCK",
15225
15416
  "CONTINUATION"
@@ -15258,7 +15449,6 @@ var init_parser = __esm(() => {
15258
15449
  }
15259
15450
  find_code_before_on_same_line(end_pos, line) {
15260
15451
  const trivia_types = [
15261
- "WHITESPACE",
15262
15452
  "COMMENT_LINE",
15263
15453
  "COMMENT_BLOCK",
15264
15454
  "CONTINUATION"
@@ -15378,12 +15568,12 @@ var init_parser = __esm(() => {
15378
15568
  if (gap > 0) {
15379
15569
  the_parts.push(" ".repeat(gap));
15380
15570
  }
15381
- } else {
15382
- const spaces_from_line_start = my_token.range.start.character;
15383
- if (spaces_from_line_start > 0) {
15384
- the_parts.push(" ".repeat(spaces_from_line_start));
15385
- } else {
15386
- the_parts.push(" ");
15571
+ } else if (my_token.range.start.line > prev_token.range.end.line) {
15572
+ const line_gap = my_token.range.start.line - prev_token.range.end.line;
15573
+ the_parts.push(`
15574
+ `.repeat(line_gap));
15575
+ if (my_token.range.start.character > 0) {
15576
+ the_parts.push(" ".repeat(my_token.range.start.character));
15387
15577
  }
15388
15578
  }
15389
15579
  }
@@ -16551,10 +16741,6 @@ function parse_macro_def_head(statement_tokens) {
16551
16741
  const run = [];
16552
16742
  while (i < statement_tokens.length) {
16553
16743
  const my_tok = statement_tokens[i];
16554
- if (my_tok.type === "WHITESPACE") {
16555
- i++;
16556
- continue;
16557
- }
16558
16744
  if (!NAME_TOKEN_TYPES.has(my_tok.type))
16559
16745
  break;
16560
16746
  if (run.length > 0 && !tokens_adjacent(run[run.length - 1], my_tok))
@@ -17046,6 +17232,57 @@ var init_macro_creating_commands = __esm(() => {
17046
17232
  ];
17047
17233
  });
17048
17234
 
17235
+ // src/utils/token-utils.ts
17236
+ function position_before2(a, b) {
17237
+ return a.line < b.line || a.line === b.line && a.character < b.character;
17238
+ }
17239
+ function position_at_or_before(a, b) {
17240
+ return a.line < b.line || a.line === b.line && a.character <= b.character;
17241
+ }
17242
+ function find_last_token_starting_at_or_before(tokens, position) {
17243
+ let low = 0;
17244
+ let high = tokens.length - 1;
17245
+ let best = -1;
17246
+ while (low <= high) {
17247
+ const mid = low + high >>> 1;
17248
+ const start = tokens[mid].range.start;
17249
+ if (position_at_or_before(start, position)) {
17250
+ best = mid;
17251
+ low = mid + 1;
17252
+ } else {
17253
+ high = mid - 1;
17254
+ }
17255
+ }
17256
+ return best;
17257
+ }
17258
+ function find_last_token_starting_before(tokens, position) {
17259
+ let low = 0;
17260
+ let high = tokens.length - 1;
17261
+ let best = -1;
17262
+ while (low <= high) {
17263
+ const mid = low + high >>> 1;
17264
+ const start = tokens[mid].range.start;
17265
+ if (position_before2(start, position)) {
17266
+ best = mid;
17267
+ low = mid + 1;
17268
+ } else {
17269
+ high = mid - 1;
17270
+ }
17271
+ }
17272
+ return best;
17273
+ }
17274
+ function find_token_index_at_position(tokens, position) {
17275
+ const candidate = find_last_token_starting_at_or_before(tokens, position);
17276
+ if (candidate === -1) {
17277
+ return -1;
17278
+ }
17279
+ const end = tokens[candidate].range.end;
17280
+ if (position_before2(position, end)) {
17281
+ return candidate;
17282
+ }
17283
+ return -1;
17284
+ }
17285
+
17049
17286
  // src/utils/statement-span.ts
17050
17287
  function next_statement_line_span(tokens, from_index) {
17051
17288
  let my_first_index = from_index + 1;
@@ -17086,13 +17323,101 @@ function next_statement_line_span(tokens, from_index) {
17086
17323
  }
17087
17324
  return { start_line, end_line };
17088
17325
  }
17089
- var LEADING_TRIVIA_TYPES, BODY_OPENER_TYPES, CONTINUATION_NEUTRAL_TYPES;
17326
+ function inline_embedded_statement_end(tokens, opener_index) {
17327
+ let my_last_content_index = opener_index;
17328
+ let my_i = opener_index + 1;
17329
+ while (my_i < tokens.length && tokens[my_i].type !== "STATEMENT_TERMINATOR" && tokens[my_i].type !== "EOF") {
17330
+ my_last_content_index = my_i;
17331
+ my_i++;
17332
+ }
17333
+ const end_line = tokens[my_last_content_index].range.end.line;
17334
+ const end_index = my_i < tokens.length && tokens[my_i].type === "STATEMENT_TERMINATOR" ? my_i : my_last_content_index;
17335
+ return { end_index, end_line };
17336
+ }
17337
+ function logical_statement_start(tokens, position, min_line = 0) {
17338
+ if (tokens === undefined || tokens.length === 0) {
17339
+ return;
17340
+ }
17341
+ let search_index = find_last_token_starting_before(tokens, position);
17342
+ while (search_index >= 0 && tokens[search_index].type === "EOF") {
17343
+ search_index--;
17344
+ }
17345
+ if (search_index < 0) {
17346
+ return;
17347
+ }
17348
+ let boundary_index = -1;
17349
+ let my_steps = 0;
17350
+ for (let my_i = search_index;my_i >= 0; my_i--) {
17351
+ if (++my_steps > MAX_STATEMENT_TOKENS) {
17352
+ return;
17353
+ }
17354
+ const my_token = tokens[my_i];
17355
+ if (my_token.range.start.line < min_line) {
17356
+ break;
17357
+ }
17358
+ if (is_swallowed_continuation_terminator(my_token, my_i > 0 && tokens[my_i - 1].type === "CONTINUATION")) {
17359
+ continue;
17360
+ }
17361
+ if (LOGICAL_BOUNDARY_TYPES.has(my_token.type)) {
17362
+ boundary_index = my_i;
17363
+ break;
17364
+ }
17365
+ }
17366
+ let boundary_opens_embedded_body = false;
17367
+ if (boundary_index >= 0) {
17368
+ const my_boundary_type = tokens[boundary_index].type;
17369
+ if (my_boundary_type === "MATA_START" || my_boundary_type === "PYTHON_START") {
17370
+ boundary_opens_embedded_body = true;
17371
+ } else if (my_boundary_type === "LBRACE") {
17372
+ for (let my_j = boundary_index - 1;my_j >= 0; my_j--) {
17373
+ const my_prev_type = tokens[my_j].type;
17374
+ if (CONTINUATION_NEUTRAL_TYPES.has(my_prev_type) || my_prev_type === "WHITESPACE") {
17375
+ continue;
17376
+ }
17377
+ boundary_opens_embedded_body = my_prev_type === "MATA_START" || my_prev_type === "PYTHON_START";
17378
+ break;
17379
+ }
17380
+ }
17381
+ }
17382
+ let first_index = -1;
17383
+ for (let my_i = boundary_index + 1;my_i < tokens.length; my_i++) {
17384
+ const my_token = tokens[my_i];
17385
+ if (my_token.type === "EOF") {
17386
+ break;
17387
+ }
17388
+ if (my_token.range.start.line < min_line) {
17389
+ continue;
17390
+ }
17391
+ if (!STATEMENT_LEADING_TRIVIA_TYPES.has(my_token.type)) {
17392
+ first_index = my_i;
17393
+ break;
17394
+ }
17395
+ }
17396
+ if (first_index !== -1 && !boundary_opens_embedded_body) {
17397
+ const my_start = tokens[first_index].range.start;
17398
+ const at_or_before_cursor = my_start.line < position.line || my_start.line === position.line && my_start.character <= position.character;
17399
+ if (at_or_before_cursor) {
17400
+ return {
17401
+ index: first_index,
17402
+ line: my_start.line,
17403
+ character: my_start.character
17404
+ };
17405
+ }
17406
+ }
17407
+ const resume_character = boundary_index >= 0 && tokens[boundary_index].range.end.line === position.line ? Math.min(tokens[boundary_index].range.end.character, position.character) : 0;
17408
+ return {
17409
+ index: first_index >= 0 ? first_index : boundary_index + 1,
17410
+ line: position.line,
17411
+ character: resume_character
17412
+ };
17413
+ }
17414
+ var LEADING_TRIVIA_TYPES, BODY_OPENER_TYPES, CONTINUATION_NEUTRAL_TYPES, LOGICAL_BOUNDARY_TYPES, STATEMENT_LEADING_TRIVIA_TYPES, MAX_STATEMENT_TOKENS = 4096;
17090
17415
  var init_statement_span = __esm(() => {
17091
17416
  LEADING_TRIVIA_TYPES = new Set([
17092
- "WHITESPACE",
17093
17417
  "COMMENT_LINE",
17094
17418
  "COMMENT_BLOCK",
17095
17419
  "CONTINUATION",
17420
+ "WHITESPACE",
17096
17421
  "STATEMENT_TERMINATOR",
17097
17422
  "DELIMIT_DIRECTIVE"
17098
17423
  ]);
@@ -17102,10 +17427,26 @@ var init_statement_span = __esm(() => {
17102
17427
  "PYTHON_START"
17103
17428
  ]);
17104
17429
  CONTINUATION_NEUTRAL_TYPES = new Set([
17105
- "WHITESPACE",
17106
17430
  "COMMENT_LINE",
17107
17431
  "COMMENT_BLOCK"
17108
17432
  ]);
17433
+ LOGICAL_BOUNDARY_TYPES = new Set([
17434
+ "STATEMENT_TERMINATOR",
17435
+ "LBRACE",
17436
+ "RBRACE",
17437
+ "DELIMIT_DIRECTIVE",
17438
+ "MATA_START",
17439
+ "PYTHON_START",
17440
+ "END_MATA",
17441
+ "END_PYTHON"
17442
+ ]);
17443
+ STATEMENT_LEADING_TRIVIA_TYPES = new Set([
17444
+ "COMMENT_LINE",
17445
+ "COMMENT_BLOCK",
17446
+ "CONTINUATION",
17447
+ "WHITESPACE",
17448
+ "STATEMENT_TERMINATOR"
17449
+ ]);
17109
17450
  });
17110
17451
 
17111
17452
  // src/analyzer/index.ts
@@ -17212,9 +17553,7 @@ class SemanticAnalyzer {
17212
17553
  };
17213
17554
  }
17214
17555
  extract_comment_directives(ast) {
17215
- for (const node of ast.nodes) {
17216
- this.extract_directives_from_node(node);
17217
- }
17556
+ this.extract_directives_from_nodes(ast.nodes);
17218
17557
  }
17219
17558
  extract_comment_directives_from_tokens(tokens, symbols, dofile_scope) {
17220
17559
  this.parse_declaration_directives_from_tokens(tokens, symbols, dofile_scope);
@@ -17348,25 +17687,45 @@ class SemanticAnalyzer {
17348
17687
  break;
17349
17688
  }
17350
17689
  }
17351
- extract_directives_from_node(node) {
17352
- if (this.has_trivia(node) && node.leadingTrivia) {
17353
- for (const trivia of node.leadingTrivia) {
17354
- this.parse_directive(trivia, node);
17690
+ extract_directives_from_nodes(nodes, outer_successor) {
17691
+ for (let my_index = 0;my_index < nodes.length; my_index++) {
17692
+ const node = nodes[my_index];
17693
+ if (this.has_trivia(node) && node.leadingTrivia) {
17694
+ for (const trivia of node.leadingTrivia) {
17695
+ this.parse_directive(trivia, node);
17696
+ }
17355
17697
  }
17356
- }
17357
- if (node.type === "program") {
17358
- for (const child of node.body) {
17359
- this.extract_directives_from_node(child);
17698
+ const following_node = nodes[my_index + 1] ?? outer_successor;
17699
+ const node_with_block_ending_trivia = node;
17700
+ if (node_with_block_ending_trivia.blockEndingTrivia) {
17701
+ for (const trivia of node_with_block_ending_trivia.blockEndingTrivia) {
17702
+ this.parse_directive(trivia, following_node);
17703
+ }
17360
17704
  }
17361
- } else if (this.is_control_flow(node)) {
17362
- for (const child of node.body) {
17363
- this.extract_directives_from_node(child);
17705
+ const the_child_body = this.directive_recursion_body(node);
17706
+ if (the_child_body) {
17707
+ this.extract_directives_from_nodes(the_child_body, following_node);
17364
17708
  }
17365
17709
  }
17366
17710
  }
17711
+ directive_recursion_body(node) {
17712
+ if (node.type === "program") {
17713
+ return node.body;
17714
+ }
17715
+ if (node.type === "frame") {
17716
+ return node.body;
17717
+ }
17718
+ if (this.is_control_flow(node)) {
17719
+ return node.body;
17720
+ }
17721
+ if (node.type === "command" && node.body) {
17722
+ return node.body;
17723
+ }
17724
+ return;
17725
+ }
17367
17726
  parse_directive(trivia, following_node) {
17368
17727
  const content = trivia.content.trim();
17369
- if (has_ignore_directive(content) || has_ignore_next_directive(content)) {
17728
+ if (following_node && (has_ignore_directive(content) || has_ignore_next_directive(content))) {
17370
17729
  const line_to_ignore = following_node.range.start.line;
17371
17730
  this.config.ignored_lines.add(line_to_ignore);
17372
17731
  }
@@ -17386,9 +17745,10 @@ class SemanticAnalyzer {
17386
17745
  if (token.range.start.line !== line) {
17387
17746
  break;
17388
17747
  }
17389
- if (token.type !== "WHITESPACE") {
17390
- return false;
17748
+ if (token.type === "WHITESPACE") {
17749
+ continue;
17391
17750
  }
17751
+ return false;
17392
17752
  }
17393
17753
  return true;
17394
17754
  }
@@ -17508,12 +17868,16 @@ class SemanticAnalyzer {
17508
17868
  }
17509
17869
  }
17510
17870
  }
17871
+ semantic_varlist(node) {
17872
+ return (node.varlist ?? []).filter((item) => item.recovery_only !== true);
17873
+ }
17511
17874
  extract_c_locals(nodes) {
17512
17875
  const c_locals = [];
17513
17876
  for (const node of nodes) {
17514
17877
  if (node.type === "command") {
17515
- if (node.fullName === "c_local" && node.varlist && node.varlist.length > 0) {
17516
- const name = node.varlist[0].name;
17878
+ const varlist = this.semantic_varlist(node);
17879
+ if (node.fullName === "c_local" && varlist.length > 0) {
17880
+ const name = varlist[0].name;
17517
17881
  if (is_valid_identifier(name)) {
17518
17882
  c_locals.push(name);
17519
17883
  }
@@ -17544,8 +17908,9 @@ class SemanticAnalyzer {
17544
17908
  const global_set = new Set;
17545
17909
  for (const node of nodes) {
17546
17910
  if (node.type === "command") {
17547
- if (node.fullName === "c_local" && node.varlist && node.varlist.length > 0) {
17548
- const macro_name = node.varlist[0].name;
17911
+ const varlist = this.semantic_varlist(node);
17912
+ if (node.fullName === "c_local" && varlist.length > 0) {
17913
+ const macro_name = varlist[0].name;
17549
17914
  if (macro_name.startsWith("`") && macro_name.endsWith("'")) {
17550
17915
  const inner_name = macro_name.slice(1, -1);
17551
17916
  const local_name = inner_name.toLowerCase();
@@ -17553,8 +17918,8 @@ class SemanticAnalyzer {
17553
17918
  local_set.add(local_name);
17554
17919
  }
17555
17920
  }
17556
- } else if (node.fullName === "global" && node.varlist && node.varlist.length > 0) {
17557
- const macro_name = node.varlist[0].name;
17921
+ } else if (node.fullName === "global" && varlist.length > 0) {
17922
+ const macro_name = varlist[0].name;
17558
17923
  if (macro_name.startsWith("`") && macro_name.endsWith("'")) {
17559
17924
  const inner_name = macro_name.slice(1, -1);
17560
17925
  const global_name = inner_name.toLowerCase();
@@ -17763,12 +18128,13 @@ class SemanticAnalyzer {
17763
18128
  };
17764
18129
  }
17765
18130
  extract_command_path(node) {
17766
- if (!node.varlist || node.varlist.length === 0) {
18131
+ const varlist = this.semantic_varlist(node);
18132
+ if (varlist.length === 0) {
17767
18133
  return null;
17768
18134
  }
17769
18135
  let raw_path = "";
17770
18136
  let has_macro = false;
17771
- const first_item = node.varlist[0];
18137
+ const first_item = varlist[0];
17772
18138
  const first_name = first_item.name;
17773
18139
  const is_complete_double_quoted = first_name.startsWith('"') && first_name.endsWith('"') && first_name.length > 1;
17774
18140
  const is_complete_single_quoted = first_name.startsWith("'") && first_name.endsWith("'") && first_name.length > 1;
@@ -17779,8 +18145,8 @@ class SemanticAnalyzer {
17779
18145
  const quote_char = first_name[0];
17780
18146
  raw_path = first_name;
17781
18147
  has_macro = raw_path.includes("`") || raw_path.includes("$");
17782
- for (let i = 1;i < node.varlist.length; i++) {
17783
- const item_name = node.varlist[i].name;
18148
+ for (let i = 1;i < varlist.length; i++) {
18149
+ const item_name = varlist[i].name;
17784
18150
  raw_path += item_name;
17785
18151
  if (item_name.includes("`") || item_name.includes("$")) {
17786
18152
  has_macro = true;
@@ -17900,28 +18266,30 @@ class SemanticAnalyzer {
17900
18266
  }
17901
18267
  }
17902
18268
  extract_scalar_symbol(node, symbols, node_index) {
17903
- if (!node.varlist || node.varlist.length === 0) {
18269
+ const varlist = this.semantic_varlist(node);
18270
+ if (varlist.length === 0) {
17904
18271
  return;
17905
18272
  }
17906
- const scalar_name = node.varlist[0].name;
17907
- this.add_or_append_definition(symbols.scalars, scalar_name, node_index, node.varlist[0].range, () => ({
18273
+ const scalar_name = varlist[0].name;
18274
+ this.add_or_append_definition(symbols.scalars, scalar_name, node_index, varlist[0].range, () => ({
17908
18275
  name: scalar_name,
17909
- location: { uri: this.uri, range: node.varlist[0].range },
18276
+ location: { uri: this.uri, range: varlist[0].range },
17910
18277
  sourceUri: this.uri,
17911
- definition_line: node.varlist[0].range.start.line
18278
+ definition_line: varlist[0].range.start.line
17912
18279
  }));
17913
18280
  }
17914
18281
  extract_matrix_symbol(node, symbols, node_index) {
17915
- if (!node.varlist || node.varlist.length === 0) {
18282
+ const varlist = this.semantic_varlist(node);
18283
+ if (varlist.length === 0) {
17916
18284
  return;
17917
18285
  }
17918
- const first = node.varlist[0]?.name;
17919
- const second = node.varlist[1]?.name;
18286
+ const first = varlist[0]?.name;
18287
+ const second = varlist[1]?.name;
17920
18288
  const matrix_name = first && first === "define" ? second : first;
17921
18289
  if (!matrix_name) {
17922
18290
  return;
17923
18291
  }
17924
- const name_range = first && first === "define" && node.varlist[1] ? node.varlist[1].range : node.varlist[0].range;
18292
+ const name_range = first && first === "define" && varlist[1] ? varlist[1].range : varlist[0].range;
17925
18293
  this.add_or_append_definition(symbols.matrices, matrix_name, node_index, name_range, () => ({
17926
18294
  name: matrix_name,
17927
18295
  location: { uri: this.uri, range: name_range },
@@ -17930,22 +18298,23 @@ class SemanticAnalyzer {
17930
18298
  }));
17931
18299
  }
17932
18300
  pick_new_variable(node) {
17933
- if (!node.varlist || node.varlist.length === 0) {
18301
+ const varlist = this.semantic_varlist(node);
18302
+ if (varlist.length === 0) {
17934
18303
  return;
17935
18304
  }
17936
- if (is_stata_storage_type(node.varlist[0].name)) {
17937
- if (node.varlist.length > 1) {
17938
- return node.varlist[1];
18305
+ if (is_stata_storage_type(varlist[0].name)) {
18306
+ if (varlist.length > 1) {
18307
+ return varlist[1];
17939
18308
  }
17940
18309
  this.current_diagnostics.push({
17941
- message: `Missing variable name after storage type \`${node.varlist[0].name}'`,
17942
- range: node.varlist[0].range,
18310
+ message: `Missing variable name after storage type \`${varlist[0].name}'`,
18311
+ range: varlist[0].range,
17943
18312
  code: "MISSING_VARIABLE_NAME" /* MISSING_VARIABLE_NAME */,
17944
18313
  severity: "error"
17945
18314
  });
17946
18315
  return;
17947
18316
  }
17948
- return node.varlist[0];
18317
+ return varlist[0];
17949
18318
  }
17950
18319
  extract_gen_variable(node, symbols) {
17951
18320
  const new_var = this.pick_new_variable(node);
@@ -17978,10 +18347,11 @@ class SemanticAnalyzer {
17978
18347
  symbols.variables.set(new_var.name, var_symbol);
17979
18348
  }
17980
18349
  extract_input_variables(node, symbols) {
17981
- if (!node.varlist) {
18350
+ const varlist = this.semantic_varlist(node);
18351
+ if (varlist.length === 0) {
17982
18352
  return;
17983
18353
  }
17984
- for (const var_node of node.varlist) {
18354
+ for (const var_node of varlist) {
17985
18355
  if (this.is_macro_reference(var_node.name)) {
17986
18356
  continue;
17987
18357
  }
@@ -18018,28 +18388,29 @@ class SemanticAnalyzer {
18018
18388
  return false;
18019
18389
  }
18020
18390
  extract_rename_variables(node, symbols) {
18021
- if (!node.varlist || node.varlist.length < 2) {
18391
+ const varlist = this.semantic_varlist(node);
18392
+ if (varlist.length < 2) {
18022
18393
  return;
18023
18394
  }
18024
- if (node.varlist.length !== 2 && !(node.varlist[0].name.startsWith("(") && node.varlist[1].name.startsWith("("))) {
18395
+ if (varlist.length !== 2 && !(varlist[0].name.startsWith("(") && varlist[1].name.startsWith("("))) {
18025
18396
  return;
18026
18397
  }
18027
- const first_item = node.varlist[0].name;
18028
- const second_item = node.varlist[1].name;
18398
+ const first_item = varlist[0].name;
18399
+ const second_item = varlist[1].name;
18029
18400
  if (first_item.startsWith("(") && second_item.startsWith("(")) {
18030
18401
  if (this.contains_wildcard(first_item) || this.contains_wildcard(second_item)) {
18031
18402
  return;
18032
18403
  }
18033
- this.extract_grouped_rename_variables(second_item, node.varlist[1].range, symbols);
18404
+ this.extract_grouped_rename_variables(second_item, varlist[1].range, symbols);
18034
18405
  return;
18035
18406
  }
18036
18407
  if (this.contains_wildcard(first_item) || this.contains_wildcard(second_item)) {
18037
18408
  return;
18038
18409
  }
18039
- if (this.has_adjacent_wildcard_token(node.varlist[0].range) || this.has_adjacent_wildcard_token(node.varlist[1].range)) {
18410
+ if (this.has_adjacent_wildcard_token(varlist[0].range) || this.has_adjacent_wildcard_token(varlist[1].range)) {
18040
18411
  return;
18041
18412
  }
18042
- const new_var = node.varlist[1];
18413
+ const new_var = varlist[1];
18043
18414
  if (this.is_macro_reference(new_var.name)) {
18044
18415
  return;
18045
18416
  }
@@ -18052,7 +18423,7 @@ class SemanticAnalyzer {
18052
18423
  symbols.variables.set(new_var.name, var_symbol);
18053
18424
  }
18054
18425
  extract_grouped_rename_variables(group_content, group_range, symbols) {
18055
- const inner = group_content.slice(1, -1).trim();
18426
+ const inner = (group_content.endsWith(")") ? group_content.slice(1, -1) : group_content.slice(1)).trim();
18056
18427
  const the_names = inner.split(/\s+/).filter((n) => n.length > 0);
18057
18428
  for (const my_name of the_names) {
18058
18429
  if (this.contains_wildcard(my_name)) {
@@ -18071,14 +18442,15 @@ class SemanticAnalyzer {
18071
18442
  }
18072
18443
  }
18073
18444
  extract_confirm_variable(node, symbols) {
18074
- if (!node.varlist || node.varlist.length < 2) {
18445
+ const varlist = this.semantic_varlist(node);
18446
+ if (varlist.length < 2) {
18075
18447
  return;
18076
18448
  }
18077
- const first_item = node.varlist[0].name.toLowerCase();
18449
+ const first_item = varlist[0].name.toLowerCase();
18078
18450
  if (first_item !== "variable" && first_item !== "var") {
18079
18451
  return;
18080
18452
  }
18081
- const var_node = node.varlist[1];
18453
+ const var_node = varlist[1];
18082
18454
  if (this.is_macro_reference(var_node.name)) {
18083
18455
  return;
18084
18456
  }
@@ -18091,10 +18463,11 @@ class SemanticAnalyzer {
18091
18463
  symbols.variables.set(var_node.name, var_symbol);
18092
18464
  }
18093
18465
  extract_tempvar_macro(node, symbols, current_scope, node_index) {
18094
- if (!node.varlist) {
18466
+ const varlist = this.semantic_varlist(node);
18467
+ if (varlist.length === 0) {
18095
18468
  return;
18096
18469
  }
18097
- for (const var_node of node.varlist) {
18470
+ for (const var_node of varlist) {
18098
18471
  this.register_local_macro(current_scope, symbols, var_node.name, node_index, var_node.range, () => ({
18099
18472
  name: var_node.name,
18100
18473
  scope: "local",
@@ -18107,8 +18480,9 @@ class SemanticAnalyzer {
18107
18480
  }
18108
18481
  }
18109
18482
  extract_unab_macro(node, symbols, current_scope, node_index) {
18110
- if (node.varlist && node.varlist.length > 0) {
18111
- const macro_node = node.varlist[0];
18483
+ const varlist = this.semantic_varlist(node);
18484
+ if (varlist.length > 0) {
18485
+ const macro_node = varlist[0];
18112
18486
  const macro_name = macro_node.name;
18113
18487
  this.register_local_macro(current_scope, symbols, macro_name, node_index, macro_node.range, () => ({
18114
18488
  name: macro_name,
@@ -18122,8 +18496,9 @@ class SemanticAnalyzer {
18122
18496
  }
18123
18497
  }
18124
18498
  extract_args_macros(node, symbols, current_scope, node_index) {
18125
- if (node.varlist && node.varlist.length > 0) {
18126
- for (const my_var_node of node.varlist) {
18499
+ const varlist = this.semantic_varlist(node);
18500
+ if (varlist.length > 0) {
18501
+ for (const my_var_node of varlist) {
18127
18502
  const macro_name = my_var_node.name;
18128
18503
  this.register_local_macro(current_scope, symbols, macro_name, node_index, my_var_node.range, () => ({
18129
18504
  name: macro_name,
@@ -18138,12 +18513,13 @@ class SemanticAnalyzer {
18138
18513
  }
18139
18514
  }
18140
18515
  extract_gettoken_macros(node, symbols, current_scope, node_index) {
18141
- if (!node.varlist || node.varlist.length === 0) {
18516
+ const varlist = this.semantic_varlist(node);
18517
+ if (varlist.length === 0) {
18142
18518
  return;
18143
18519
  }
18144
- const max_macros = Math.min(node.varlist.length, 2);
18520
+ const max_macros = Math.min(varlist.length, 2);
18145
18521
  for (let i = 0;i < max_macros; i++) {
18146
- const my_var_node = node.varlist[i];
18522
+ const my_var_node = varlist[i];
18147
18523
  const macro_name = my_var_node.name;
18148
18524
  if (!is_valid_identifier(macro_name)) {
18149
18525
  continue;
@@ -18160,13 +18536,14 @@ class SemanticAnalyzer {
18160
18536
  }
18161
18537
  }
18162
18538
  extract_file_read_macro(node, symbols, current_scope, node_index) {
18163
- if (!node.varlist || node.varlist.length < 3) {
18539
+ const varlist = this.semantic_varlist(node);
18540
+ if (varlist.length < 3) {
18164
18541
  return;
18165
18542
  }
18166
- if (node.varlist[0].name !== "read") {
18543
+ if (varlist[0].name !== "read") {
18167
18544
  return;
18168
18545
  }
18169
- const macro_node = node.varlist[2];
18546
+ const macro_node = varlist[2];
18170
18547
  const macro_name = macro_node.name;
18171
18548
  if (!is_valid_identifier(macro_name)) {
18172
18549
  return;
@@ -18240,6 +18617,9 @@ class SemanticAnalyzer {
18240
18617
  if (!builtin_cmd && !program_options)
18241
18618
  return false;
18242
18619
  for (const my_option of node.options) {
18620
+ if (my_option.argument_unclosed) {
18621
+ continue;
18622
+ }
18243
18623
  let matches = false;
18244
18624
  if (builtin_cmd) {
18245
18625
  for (const opt of builtin_cmd.local_options) {
@@ -18266,7 +18646,7 @@ class SemanticAnalyzer {
18266
18646
  if (node.fullName !== "macro" && node.fullName !== "mac") {
18267
18647
  return { names: [], unknown: false };
18268
18648
  }
18269
- const the_args = node.varlist ?? [];
18649
+ const the_args = this.semantic_varlist(node);
18270
18650
  if (the_args.length < 2 || !/^dr(o(p)?)?$/.test(the_args[0].name)) {
18271
18651
  return { names: [], unknown: false };
18272
18652
  }
@@ -18297,18 +18677,20 @@ class SemanticAnalyzer {
18297
18677
  names.push(name);
18298
18678
  };
18299
18679
  if (cmd_name === "tempvar" || cmd_name === "tempfile" || cmd_name === "tempname" || cmd_name === "args") {
18300
- for (const my_var of node.varlist ?? [])
18680
+ for (const my_var of this.semantic_varlist(node))
18301
18681
  push_literal(my_var.name);
18302
18682
  } else if (cmd_name === "unab") {
18303
- push_literal(node.varlist?.[0]?.name);
18683
+ push_literal(this.semantic_varlist(node)[0]?.name);
18304
18684
  } else if (cmd_name === "gettoken" || cmd_name === "gettok") {
18305
- const max_macros = Math.min(node.varlist?.length ?? 0, 2);
18685
+ const varlist = this.semantic_varlist(node);
18686
+ const max_macros = Math.min(varlist.length, 2);
18306
18687
  for (let i = 0;i < max_macros; i++) {
18307
- push_literal(node.varlist[i].name);
18688
+ push_literal(varlist[i].name);
18308
18689
  }
18309
18690
  } else if (cmd_name === "file" || cmd_name === "fil" || cmd_name === "fi") {
18310
- if ((node.varlist?.length ?? 0) >= 3 && node.varlist[0].name === "read") {
18311
- push_literal(node.varlist[2].name);
18691
+ const varlist = this.semantic_varlist(node);
18692
+ if (varlist.length >= 3 && varlist[0].name === "read") {
18693
+ push_literal(varlist[2].name);
18312
18694
  }
18313
18695
  }
18314
18696
  return names;
@@ -18347,6 +18729,9 @@ class SemanticAnalyzer {
18347
18729
  }
18348
18730
  const the_created = [];
18349
18731
  for (const option of node.options) {
18732
+ if (option.argument_unclosed) {
18733
+ continue;
18734
+ }
18350
18735
  const parse_result = parse_option_argument(option.argument);
18351
18736
  if (!parse_result.is_literal || !parse_result.identifier) {
18352
18737
  continue;
@@ -18696,19 +19081,17 @@ class SemanticAnalyzer {
18696
19081
  if (cmd_name === "generate" || cmd_name === "gen" || cmd_name === "egen" || cmd_name === "input" || cmd_name === "tempvar" || cmd_name === "unab") {
18697
19082
  return;
18698
19083
  }
18699
- if (node.varlist) {
18700
- for (const var_node of node.varlist) {
18701
- const is_defined = this.is_variable_defined(var_node.name, symbols, var_node.range.start.line);
18702
- if (!is_defined) {
18703
- diagnostics.push({
18704
- message: format_undefined_variable_message(var_node.name),
18705
- range: var_node.range,
18706
- code: "UNDEFINED_VARIABLE" /* UNDEFINED_VARIABLE */,
18707
- severity: "information",
18708
- symbol_name: var_node.name,
18709
- reference_kind: "variable"
18710
- });
18711
- }
19084
+ for (const var_node of this.semantic_varlist(node)) {
19085
+ const is_defined = this.is_variable_defined(var_node.name, symbols, var_node.range.start.line);
19086
+ if (!is_defined) {
19087
+ diagnostics.push({
19088
+ message: format_undefined_variable_message(var_node.name),
19089
+ range: var_node.range,
19090
+ code: "UNDEFINED_VARIABLE" /* UNDEFINED_VARIABLE */,
19091
+ severity: "information",
19092
+ symbol_name: var_node.name,
19093
+ reference_kind: "variable"
19094
+ });
18712
19095
  }
18713
19096
  }
18714
19097
  }
@@ -19750,12 +20133,10 @@ class ContextTracker {
19750
20133
  is_single_line: my_token.type === "MATA_INLINE"
19751
20134
  };
19752
20135
  if (my_token.type === "MATA_INLINE") {
19753
- my_current_range.end_delimiter = {
19754
- command: my_token.value,
19755
- range: my_token.range
19756
- };
19757
- this.context_ranges.push(this.complete_context_range_from_token(my_current_range));
20136
+ const my_inline_end = inline_embedded_statement_end(tokens, my_i);
20137
+ this.context_ranges.push(this.complete_context_range_from_token(my_current_range, my_inline_end.end_line));
19758
20138
  my_current_range = null;
20139
+ my_i = my_inline_end.end_index;
19759
20140
  } else {
19760
20141
  this.context_stack.push("mata" /* MATA */);
19761
20142
  my_current_context = "mata" /* MATA */;
@@ -19777,12 +20158,10 @@ class ContextTracker {
19777
20158
  is_single_line: my_token.type === "PYTHON_INLINE"
19778
20159
  };
19779
20160
  if (my_token.type === "PYTHON_INLINE") {
19780
- my_current_range.end_delimiter = {
19781
- command: my_token.value,
19782
- range: my_token.range
19783
- };
19784
- this.context_ranges.push(this.complete_context_range_from_token(my_current_range));
20161
+ const my_inline_end = inline_embedded_statement_end(tokens, my_i);
20162
+ this.context_ranges.push(this.complete_context_range_from_token(my_current_range, my_inline_end.end_line));
19785
20163
  my_current_range = null;
20164
+ my_i = my_inline_end.end_index;
19786
20165
  } else {
19787
20166
  this.context_stack.push("python" /* PYTHON */);
19788
20167
  my_current_context = "python" /* PYTHON */;
@@ -20086,11 +20465,11 @@ class ContextTracker {
20086
20465
  }
20087
20466
  return false;
20088
20467
  }
20089
- complete_context_range_from_token(partial_range) {
20468
+ complete_context_range_from_token(partial_range, single_line_end_line) {
20090
20469
  const my_start_line = partial_range.start_delimiter.range.start.line;
20091
20470
  let my_end_line;
20092
20471
  if (partial_range.is_single_line) {
20093
- my_end_line = my_start_line;
20472
+ my_end_line = single_line_end_line ?? my_start_line;
20094
20473
  } else {
20095
20474
  if (partial_range.end_delimiter) {
20096
20475
  my_end_line = partial_range.end_delimiter.range.start.line - 1;
@@ -20278,6 +20657,7 @@ var init_context_tracker = __esm(() => {
20278
20657
  init_types2();
20279
20658
  init_types();
20280
20659
  init_block_comment_utils();
20660
+ init_statement_span();
20281
20661
  CONTEXT_TRIVIA_TOKEN_TYPES = new Set([
20282
20662
  "COMMENT_LINE",
20283
20663
  "COMMENT_BLOCK",
@@ -20328,6 +20708,19 @@ async function with_parse_timeout(operation, timeout_ms = PARSE_TIMEOUT_MS) {
20328
20708
  }
20329
20709
  var PARSE_TIMEOUT_MS = 5000;
20330
20710
 
20711
+ // src/utils/polling-cancellation-token.ts
20712
+ function polling_cancellation_token(should_continue, parent) {
20713
+ return {
20714
+ get isCancellationRequested() {
20715
+ return parent?.isCancellationRequested === true || !should_continue();
20716
+ },
20717
+ onCancellationRequested: no_op_cancellation_event
20718
+ };
20719
+ }
20720
+ var no_op_cancellation_event = () => ({ dispose: () => {
20721
+ return;
20722
+ } });
20723
+
20331
20724
  // src/document-store.ts
20332
20725
  import * as fs4 from "fs";
20333
20726
  import * as path4 from "path";
@@ -20395,7 +20788,7 @@ class DocumentStore {
20395
20788
  throw new Error("DocumentStore has been disposed");
20396
20789
  }
20397
20790
  }
20398
- async open(uri, content, version, workspace_symbols, scope_resolver_config) {
20791
+ async open(uri, content, version, workspace_symbols, scope_resolver_config, is_operation_current) {
20399
20792
  this.check_disposed();
20400
20793
  const generation = (this.generations.get(uri) ?? 0) + 1;
20401
20794
  this.generations.set(uri, generation);
@@ -20410,9 +20803,15 @@ class DocumentStore {
20410
20803
  if (this.disposed) {
20411
20804
  return;
20412
20805
  }
20806
+ if (is_operation_current && !is_operation_current()) {
20807
+ return;
20808
+ }
20413
20809
  this.evict_if_needed(content.length);
20414
- const outcome = await this.create_document_state(uri, content, version, workspace_symbols, scope_resolver_config);
20415
- this.commit_state(uri, outcome, generation);
20810
+ const outcome = await this.create_document_state(uri, content, version, workspace_symbols, scope_resolver_config, is_operation_current);
20811
+ if (!outcome) {
20812
+ return;
20813
+ }
20814
+ this.commit_state(uri, outcome, generation, is_operation_current);
20416
20815
  };
20417
20816
  const promise = operation();
20418
20817
  this.active_updates.set(uri, promise);
@@ -20425,7 +20824,7 @@ class DocumentStore {
20425
20824
  this.decrement_in_flight(uri);
20426
20825
  }
20427
20826
  }
20428
- async update(uri, changes, version, workspace_symbols, scope_resolver_config) {
20827
+ async update(uri, changes, version, workspace_symbols, scope_resolver_config, is_operation_current) {
20429
20828
  this.check_disposed();
20430
20829
  const generation = (this.generations.get(uri) ?? 0) + 1;
20431
20830
  this.generations.set(uri, generation);
@@ -20440,6 +20839,9 @@ class DocumentStore {
20440
20839
  if (this.disposed) {
20441
20840
  return;
20442
20841
  }
20842
+ if (is_operation_current && !is_operation_current()) {
20843
+ return;
20844
+ }
20443
20845
  const state = this.documents.get(uri);
20444
20846
  if (!state) {
20445
20847
  return;
@@ -20460,8 +20862,11 @@ class DocumentStore {
20460
20862
  this.metrics.cache_hits++;
20461
20863
  return;
20462
20864
  }
20463
- const new_outcome = await this.create_document_state(uri, new_content, version, workspace_symbols, scope_resolver_config);
20464
- this.commit_state(uri, new_outcome, generation);
20865
+ const new_outcome = await this.create_document_state(uri, new_content, version, workspace_symbols, scope_resolver_config, is_operation_current);
20866
+ if (!new_outcome) {
20867
+ return;
20868
+ }
20869
+ this.commit_state(uri, new_outcome, generation, is_operation_current);
20465
20870
  };
20466
20871
  const promise = operation();
20467
20872
  this.active_updates.set(uri, promise);
@@ -20504,10 +20909,13 @@ class DocumentStore {
20504
20909
  get_metrics() {
20505
20910
  return { ...this.metrics };
20506
20911
  }
20507
- commit_state(uri, outcome, generation) {
20912
+ commit_state(uri, outcome, generation, is_operation_current) {
20508
20913
  if (this.disposed) {
20509
20914
  return false;
20510
20915
  }
20916
+ if (is_operation_current && !is_operation_current()) {
20917
+ return false;
20918
+ }
20511
20919
  const closed_gen = this.closed_generations.get(uri);
20512
20920
  if (closed_gen !== undefined && generation <= closed_gen) {
20513
20921
  return false;
@@ -20552,11 +20960,11 @@ class DocumentStore {
20552
20960
  return;
20553
20961
  }
20554
20962
  }
20555
- async recover_staged_effects_with_timeout(content, uri, scope_resolver_config) {
20963
+ async recover_staged_effects_with_timeout(content, uri, scope_resolver_config, is_operation_current) {
20556
20964
  try {
20557
20965
  const my_directive_parser = new DirectiveParser;
20558
- const my_result = await with_parse_timeout(() => my_directive_parser.parse(content, uri));
20559
- if (!my_result.success || my_result.timed_out || !my_result.result) {
20966
+ const my_result = await with_parse_timeout(() => is_operation_current?.() === false ? null : my_directive_parser.parse(content, uri));
20967
+ if (is_operation_current?.() === false || !my_result.success || my_result.timed_out || !my_result.result) {
20560
20968
  return;
20561
20969
  }
20562
20970
  return this.stage_cross_file_effects(my_result.result, scope_resolver_config);
@@ -20673,16 +21081,24 @@ class DocumentStore {
20673
21081
  }
20674
21082
  return total;
20675
21083
  }
20676
- async create_document_state(uri, content, version, workspace_symbols, scope_resolver_config) {
21084
+ async create_document_state(uri, content, version, workspace_symbols, scope_resolver_config, is_operation_current) {
20677
21085
  const start_time = Date.now();
20678
21086
  this.metrics.parse_count++;
21087
+ const operation_is_current = () => is_operation_current?.() !== false;
21088
+ const cancellation_token = is_operation_current ? polling_cancellation_token(is_operation_current) : undefined;
20679
21089
  const lexer = new StataLexer;
20680
21090
  const parser = new StataParser;
20681
21091
  const analyzer = new SemanticAnalyzer;
20682
21092
  const effective_scope_resolver_config = scope_resolver_config ?? this.scope_resolver_config;
20683
- const lex_result = await with_parse_timeout(() => lexer.tokenize(content));
21093
+ const lex_result = await with_parse_timeout(() => operation_is_current() ? lexer.tokenize(content) : null);
21094
+ if (!operation_is_current() || lex_result.result === null) {
21095
+ return;
21096
+ }
20684
21097
  if (!lex_result.success || lex_result.timed_out) {
20685
- const recovered_staged_effects = lex_result.timed_out ? undefined : await this.recover_staged_effects_with_timeout(content, uri, effective_scope_resolver_config);
21098
+ const recovered_staged_effects = lex_result.timed_out ? undefined : await this.recover_staged_effects_with_timeout(content, uri, effective_scope_resolver_config, is_operation_current);
21099
+ if (!operation_is_current()) {
21100
+ return;
21101
+ }
20686
21102
  return {
20687
21103
  state: this.create_error_state(uri, content, version, lex_result.error || "Lexer timeout", lex_result.result?.line_offsets),
20688
21104
  staged_effects: recovered_staged_effects
@@ -20692,7 +21108,10 @@ class DocumentStore {
20692
21108
  my_context_tracker.initialize_from_tokens(lex_result.result.tokens, content);
20693
21109
  const context_ranges = my_context_tracker.get_all_context_ranges();
20694
21110
  this.assert_ranges_sorted(context_ranges);
20695
- const parse_result = await with_parse_timeout(() => parser.parse(lex_result.result.tokens, my_context_tracker));
21111
+ const parse_result = await with_parse_timeout(() => operation_is_current() ? parser.parse(lex_result.result.tokens, my_context_tracker) : null);
21112
+ if (!operation_is_current() || parse_result.result === null) {
21113
+ return;
21114
+ }
20696
21115
  if (!parse_result.success || parse_result.timed_out) {
20697
21116
  const recovered_staged_effects = this.recover_staged_effects_from_tokens(content, uri, lex_result.result.tokens, effective_scope_resolver_config);
20698
21117
  return {
@@ -20710,16 +21129,22 @@ class DocumentStore {
20710
21129
  resolved_working_directory = this.resolve_working_directory(directive_result.working_directory, uri);
20711
21130
  } else if (this.scope_resolver) {
20712
21131
  try {
20713
- const scope_result = await this.scope_resolver.resolve(uri, content, effective_scope_resolver_config, undefined, { register_dependencies: false });
21132
+ const scope_result = await this.scope_resolver.resolve(uri, content, effective_scope_resolver_config, cancellation_token, { register_dependencies: false });
20714
21133
  if (scope_result.inherited_working_directory) {
20715
21134
  resolved_working_directory = scope_result.inherited_working_directory;
20716
21135
  }
20717
21136
  } catch {}
20718
21137
  }
20719
21138
  } catch {}
20720
- const analyze_result = await with_parse_timeout(() => analyzer.analyze(parse_result.result.ast, uri, workspace_symbols, {
21139
+ if (!operation_is_current()) {
21140
+ return;
21141
+ }
21142
+ const analyze_result = await with_parse_timeout(() => operation_is_current() ? analyzer.analyze(parse_result.result.ast, uri, workspace_symbols, {
20721
21143
  working_directory: resolved_working_directory
20722
- }, lex_result.result.tokens));
21144
+ }, lex_result.result.tokens) : null);
21145
+ if (!operation_is_current() || analyze_result.result === null) {
21146
+ return;
21147
+ }
20723
21148
  const elapsed_ms = Date.now() - start_time;
20724
21149
  this.metrics.parse_total_ms += elapsed_ms;
20725
21150
  if (!analyze_result.success || analyze_result.timed_out) {
@@ -20961,6 +21386,221 @@ var init_document_store = __esm(() => {
20961
21386
  import_vscode_languageserver = __toESM(require_main4(), 1);
20962
21387
  });
20963
21388
 
21389
+ // src/utils/lru-cache.ts
21390
+ class LRUCache {
21391
+ cache = new Map;
21392
+ max_size;
21393
+ hits = 0;
21394
+ misses = 0;
21395
+ evictions = 0;
21396
+ current_generation = 0;
21397
+ constructor(max_size = 100) {
21398
+ this.max_size = max_size;
21399
+ }
21400
+ get(key) {
21401
+ const entry = this.cache.get(key);
21402
+ if (entry !== undefined) {
21403
+ this.hits++;
21404
+ entry.access_count++;
21405
+ this.cache.delete(key);
21406
+ this.cache.set(key, entry);
21407
+ return entry.value;
21408
+ } else {
21409
+ this.misses++;
21410
+ }
21411
+ return;
21412
+ }
21413
+ set(key, value) {
21414
+ this.cache.delete(key);
21415
+ if (this.cache.size >= this.max_size) {
21416
+ this.evict_one();
21417
+ }
21418
+ this.cache.set(key, {
21419
+ value,
21420
+ generation: this.current_generation,
21421
+ access_count: 1
21422
+ });
21423
+ }
21424
+ evict_one() {
21425
+ let victim_key;
21426
+ let victim_score = Infinity;
21427
+ for (const [key, entry] of this.cache) {
21428
+ const score = entry.generation * 1000 + entry.access_count;
21429
+ if (score < victim_score) {
21430
+ victim_score = score;
21431
+ victim_key = key;
21432
+ }
21433
+ }
21434
+ if (victim_key !== undefined) {
21435
+ this.cache.delete(victim_key);
21436
+ this.evictions++;
21437
+ }
21438
+ }
21439
+ advance_generation() {
21440
+ this.current_generation++;
21441
+ }
21442
+ trim_to_top_n(n) {
21443
+ if (this.cache.size <= n)
21444
+ return;
21445
+ const entries = Array.from(this.cache.entries()).sort((a, b) => b[1].access_count - a[1].access_count);
21446
+ this.cache.clear();
21447
+ for (let i = 0;i < Math.min(n, entries.length); i++) {
21448
+ this.cache.set(entries[i][0], entries[i][1]);
21449
+ }
21450
+ this.evictions += entries.length - n;
21451
+ }
21452
+ has(key) {
21453
+ return this.cache.has(key);
21454
+ }
21455
+ clear() {
21456
+ this.cache.clear();
21457
+ }
21458
+ size() {
21459
+ return this.cache.size;
21460
+ }
21461
+ get_stats() {
21462
+ return { hits: this.hits, misses: this.misses, evictions: this.evictions };
21463
+ }
21464
+ }
21465
+
21466
+ class CompletionPrefixCache {
21467
+ cache;
21468
+ command_db_version = 0;
21469
+ workspace_version = 0;
21470
+ top_n_threshold;
21471
+ constructor(max_size = 100, top_n_threshold = 200) {
21472
+ this.cache = new LRUCache(max_size);
21473
+ this.top_n_threshold = top_n_threshold;
21474
+ }
21475
+ make_key(prefix, context, document_version = 0) {
21476
+ return `${context}:${prefix}:${this.command_db_version}:${this.workspace_version}:${document_version}`;
21477
+ }
21478
+ get_with_context(prefix, context, document_version = 0) {
21479
+ return this.cache.get(this.make_key(prefix, context, document_version));
21480
+ }
21481
+ set_with_context(prefix, context, value, document_version = 0) {
21482
+ const trimmed_value = this.apply_limit(value);
21483
+ this.cache.set(this.make_key(prefix, context, document_version), trimmed_value);
21484
+ return trimmed_value;
21485
+ }
21486
+ apply_limit(value) {
21487
+ if (this.top_n_threshold > 0 && value.length > this.top_n_threshold) {
21488
+ return value.slice(0, this.top_n_threshold);
21489
+ }
21490
+ return value;
21491
+ }
21492
+ has_with_context(prefix, context, document_version = 0) {
21493
+ return this.cache.has(this.make_key(prefix, context, document_version));
21494
+ }
21495
+ invalidate_on_db_change(new_version) {
21496
+ if (new_version !== this.command_db_version) {
21497
+ this.command_db_version = new_version;
21498
+ this.cache.clear();
21499
+ this.cache.advance_generation();
21500
+ }
21501
+ }
21502
+ invalidate_on_workspace_change(new_version) {
21503
+ if (new_version !== this.workspace_version) {
21504
+ this.workspace_version = new_version;
21505
+ this.cache.clear();
21506
+ this.cache.advance_generation();
21507
+ }
21508
+ }
21509
+ clear() {
21510
+ this.cache.clear();
21511
+ }
21512
+ size() {
21513
+ return this.cache.size();
21514
+ }
21515
+ get_stats() {
21516
+ return this.cache.get_stats();
21517
+ }
21518
+ }
21519
+ var BoundedLruMap;
21520
+ var init_lru_cache = __esm(() => {
21521
+ BoundedLruMap = class BoundedLruMap {
21522
+ map = new Map;
21523
+ max_size;
21524
+ on_evict;
21525
+ constructor(max_size, options) {
21526
+ this.max_size = Math.max(1, Math.floor(max_size));
21527
+ this.on_evict = options?.on_evict;
21528
+ }
21529
+ get(key) {
21530
+ if (!this.map.has(key)) {
21531
+ return;
21532
+ }
21533
+ const value = this.map.get(key);
21534
+ this.map.delete(key);
21535
+ this.map.set(key, value);
21536
+ return value;
21537
+ }
21538
+ peek(key) {
21539
+ return this.map.get(key);
21540
+ }
21541
+ has(key) {
21542
+ return this.map.has(key);
21543
+ }
21544
+ touch(key) {
21545
+ if (!this.map.has(key)) {
21546
+ return;
21547
+ }
21548
+ const value = this.map.get(key);
21549
+ this.map.delete(key);
21550
+ this.map.set(key, value);
21551
+ }
21552
+ set(key, value) {
21553
+ if (this.map.has(key)) {
21554
+ this.map.delete(key);
21555
+ } else if (this.map.size >= this.max_size) {
21556
+ this.evict_oldest();
21557
+ }
21558
+ this.map.set(key, value);
21559
+ return this;
21560
+ }
21561
+ delete(key) {
21562
+ return this.map.delete(key);
21563
+ }
21564
+ clear() {
21565
+ this.map.clear();
21566
+ }
21567
+ get size() {
21568
+ return this.map.size;
21569
+ }
21570
+ get capacity() {
21571
+ return this.max_size;
21572
+ }
21573
+ set_max_size(new_max_size) {
21574
+ this.max_size = Math.max(1, Math.floor(new_max_size));
21575
+ while (this.map.size > this.max_size) {
21576
+ this.evict_oldest();
21577
+ }
21578
+ }
21579
+ keys() {
21580
+ return this.map.keys();
21581
+ }
21582
+ values() {
21583
+ return this.map.values();
21584
+ }
21585
+ entries() {
21586
+ return this.map.entries();
21587
+ }
21588
+ [Symbol.iterator]() {
21589
+ return this.map[Symbol.iterator]();
21590
+ }
21591
+ evict_oldest() {
21592
+ const oldest = this.map.keys().next();
21593
+ if (oldest.done) {
21594
+ return;
21595
+ }
21596
+ const oldest_key = oldest.value;
21597
+ const oldest_value = this.map.get(oldest_key);
21598
+ this.map.delete(oldest_key);
21599
+ this.on_evict?.(oldest_key, oldest_value);
21600
+ }
21601
+ };
21602
+ });
21603
+
20964
21604
  // src/utils/dofile-locals.ts
20965
21605
  function is_dofile_local(symbol) {
20966
21606
  return symbol.containingScope === "dofile";
@@ -21261,14 +21901,20 @@ class ForwardScopeResolver {
21261
21901
  workspace_roots = [];
21262
21902
  resolve_fs;
21263
21903
  forward_closure_memo_enabled = true;
21264
- forward_closure_memo = new Map;
21904
+ forward_closure_memo = new BoundedLruMap(DEFAULT_MAX_CACHED_FORWARD_CLOSURES, {
21905
+ on_evict: (key, entry) => {
21906
+ this.memo_metrics.evictions++;
21907
+ this.prune_memo_uri_to_keys_for_entry(key, entry);
21908
+ }
21909
+ });
21265
21910
  memo_uri_to_keys = new Map;
21266
21911
  last_seen_graph_version;
21267
21912
  memo_invalidation_epoch = 0;
21268
21913
  memo_metrics = {
21269
21914
  hits: 0,
21270
21915
  misses: 0,
21271
- invalidations: 0
21916
+ invalidations: 0,
21917
+ evictions: 0
21272
21918
  };
21273
21919
  standalone_in_flight = new Set;
21274
21920
  active_probe_collectors = [];
@@ -21291,8 +21937,16 @@ class ForwardScopeResolver {
21291
21937
  get_forward_closure_metrics() {
21292
21938
  return { ...this.memo_metrics };
21293
21939
  }
21940
+ get_forward_closure_memo_size() {
21941
+ return this.forward_closure_memo.size;
21942
+ }
21294
21943
  reset_forward_closure_metrics() {
21295
- this.memo_metrics = { hits: 0, misses: 0, invalidations: 0 };
21944
+ this.memo_metrics = {
21945
+ hits: 0,
21946
+ misses: 0,
21947
+ invalidations: 0,
21948
+ evictions: 0
21949
+ };
21296
21950
  }
21297
21951
  invalidate_forward_closure_for_uri(uri) {
21298
21952
  this.memo_invalidation_epoch++;
@@ -21316,12 +21970,16 @@ class ForwardScopeResolver {
21316
21970
  this.memo_uri_to_keys.clear();
21317
21971
  }
21318
21972
  delete_memo_entry(key) {
21319
- const my_entry = this.forward_closure_memo.get(key);
21973
+ const my_entry = this.forward_closure_memo.peek(key);
21320
21974
  if (!my_entry) {
21321
21975
  return false;
21322
21976
  }
21323
21977
  this.forward_closure_memo.delete(key);
21324
- for (const my_dep_uri of my_entry.dependent_uris) {
21978
+ this.prune_memo_uri_to_keys_for_entry(key, my_entry);
21979
+ return true;
21980
+ }
21981
+ prune_memo_uri_to_keys_for_entry(key, entry) {
21982
+ for (const my_dep_uri of entry.dependent_uris) {
21325
21983
  const my_key_set = this.memo_uri_to_keys.get(my_dep_uri);
21326
21984
  if (my_key_set) {
21327
21985
  my_key_set.delete(key);
@@ -21330,7 +21988,9 @@ class ForwardScopeResolver {
21330
21988
  }
21331
21989
  }
21332
21990
  }
21333
- return true;
21991
+ }
21992
+ set_forward_closure_memo_capacity(max_entries) {
21993
+ this.forward_closure_memo.set_max_size(max_entries);
21334
21994
  }
21335
21995
  is_memo_usable() {
21336
21996
  return this.dependency_graph === undefined || this.dependency_graph.is_scan_complete();
@@ -21743,6 +22403,10 @@ class ForwardScopeResolver {
21743
22403
  });
21744
22404
  }
21745
22405
  store_memo_entry(key, entry) {
22406
+ const my_previous = this.forward_closure_memo.peek(key);
22407
+ if (my_previous) {
22408
+ this.prune_memo_uri_to_keys_for_entry(key, my_previous);
22409
+ }
21746
22410
  this.forward_closure_memo.set(key, entry);
21747
22411
  for (const my_dep_uri of entry.dependent_uris) {
21748
22412
  let my_key_set = this.memo_uri_to_keys.get(my_dep_uri);
@@ -21916,13 +22580,14 @@ class ForwardScopeResolver {
21916
22580
  this.clear_forward_closure_memo();
21917
22581
  }
21918
22582
  }
21919
- var DEFAULT_CONFIG;
22583
+ var DEFAULT_CONFIG, DEFAULT_MAX_CACHED_FORWARD_CLOSURES = 2000;
21920
22584
  var init_forward_scope_resolver = __esm(() => {
21921
22585
  init_esm();
21922
22586
  init_types();
21923
22587
  init_analyzer();
21924
22588
  init_file_path_utils();
21925
22589
  init_workspace_roots();
22590
+ init_lru_cache();
21926
22591
  init_dofile_locals();
21927
22592
  init_visible_symbols();
21928
22593
  DEFAULT_CONFIG = {
@@ -21981,13 +22646,23 @@ class ScopeResolver {
21981
22646
  workspace_roots = [];
21982
22647
  dependency_graph;
21983
22648
  resolve_fs;
21984
- constructor(logger2, content_provider) {
22649
+ constructor(logger2, content_provider, cache_capacities) {
21985
22650
  this.directive_parser = new DirectiveParser;
21986
22651
  this.lexer = new StataLexer;
21987
22652
  this.parser = new StataParser;
21988
22653
  this.analyzer = new SemanticAnalyzer;
21989
- this.file_cache = new Map;
21990
- this.scope_cache = new Map;
22654
+ this.file_cache = new BoundedLruMap(cache_capacities?.max_cached_files ?? DEFAULT_MAX_CACHED_FILES, {
22655
+ on_evict: (_key, entry) => {
22656
+ this.cache_metrics.file.evictions++;
22657
+ this.forward_scope_resolver?.invalidate_forward_closure_for_uri?.(entry.uri);
22658
+ }
22659
+ });
22660
+ this.scope_cache = new BoundedLruMap(cache_capacities?.max_cached_scopes ?? DEFAULT_MAX_CACHED_SCOPES, {
22661
+ on_evict: (key) => {
22662
+ this.cache_metrics.scope.evictions++;
22663
+ this.prune_uri_to_cache_keys_for_key(key);
22664
+ }
22665
+ });
21991
22666
  this.uri_to_cache_keys = new Map;
21992
22667
  this.cache_metrics = this.create_metrics();
21993
22668
  this.logger = logger2;
@@ -22082,8 +22757,8 @@ class ScopeResolver {
22082
22757
  }
22083
22758
  create_metrics() {
22084
22759
  const metrics = {
22085
- scope: { hits: 0, misses: 0, invalidations: 0 },
22086
- file: { hits: 0, misses: 0, invalidations: 0 }
22760
+ scope: { hits: 0, misses: 0, invalidations: 0, evictions: 0 },
22761
+ file: { hits: 0, misses: 0, invalidations: 0, evictions: 0 }
22087
22762
  };
22088
22763
  return {
22089
22764
  ...metrics,
@@ -22098,6 +22773,24 @@ class ScopeResolver {
22098
22773
  }
22099
22774
  };
22100
22775
  }
22776
+ set_cache_capacities(capacities) {
22777
+ if (capacities.max_cached_files !== undefined) {
22778
+ this.file_cache.set_max_size(capacities.max_cached_files);
22779
+ }
22780
+ if (capacities.max_cached_scopes !== undefined) {
22781
+ this.scope_cache.set_max_size(capacities.max_cached_scopes);
22782
+ }
22783
+ }
22784
+ prune_uri_to_cache_keys_for_key(cache_key) {
22785
+ const key_uri = this.extract_uri_from_cache_key(cache_key);
22786
+ const key_set = this.uri_to_cache_keys.get(key_uri);
22787
+ if (key_set) {
22788
+ key_set.delete(cache_key);
22789
+ if (key_set.size === 0) {
22790
+ this.uri_to_cache_keys.delete(key_uri);
22791
+ }
22792
+ }
22793
+ }
22101
22794
  make_file_cache_key(uri, working_directory) {
22102
22795
  return working_directory ? `${uri}|${working_directory}` : uri;
22103
22796
  }
@@ -22110,14 +22803,7 @@ class ScopeResolver {
22110
22803
  }
22111
22804
  for (const my_key of keys_to_remove) {
22112
22805
  this.scope_cache.delete(my_key);
22113
- const key_uri = this.extract_uri_from_cache_key(my_key);
22114
- const key_set = this.uri_to_cache_keys.get(key_uri);
22115
- if (key_set) {
22116
- key_set.delete(my_key);
22117
- if (key_set.size === 0) {
22118
- this.uri_to_cache_keys.delete(key_uri);
22119
- }
22120
- }
22806
+ this.prune_uri_to_cache_keys_for_key(my_key);
22121
22807
  }
22122
22808
  return keys_to_remove.length;
22123
22809
  }
@@ -22448,22 +23134,23 @@ class ScopeResolver {
22448
23134
  used_auto_parents: has_auto_parents
22449
23135
  } = this.get_effective_backward_directives(file_uri, my_directives, my_config, is_standalone);
22450
23136
  const scan_complete_at_resolve_time = this.dependency_graph?.is_scan_complete();
23137
+ const cancelled_scope = () => ({
23138
+ chain: the_chain,
23139
+ symbols: create_empty_symbol_table(),
23140
+ out_of_scope_symbols: the_out_of_scope,
23141
+ diagnostics: [],
23142
+ has_directives,
23143
+ has_auto_parents,
23144
+ is_standalone,
23145
+ scan_complete_at_resolve_time
23146
+ });
22451
23147
  const normalized_directives = this.normalize_directives(effective_directives, the_diagnostics);
22452
23148
  if (options?.register_dependencies ?? true) {
22453
23149
  this.apply_normalized_backward_directives(file_uri, normalized_directives);
22454
23150
  }
22455
23151
  const directive_result = await this.follow_directives(normalized_directives, file_uri, visited, the_chain, the_diagnostics, the_out_of_scope, 1, my_config, request_cache, token);
22456
23152
  if (token?.isCancellationRequested) {
22457
- return {
22458
- chain: the_chain,
22459
- symbols: create_empty_symbol_table(),
22460
- out_of_scope_symbols: the_out_of_scope,
22461
- diagnostics: [],
22462
- has_directives,
22463
- has_auto_parents,
22464
- is_standalone,
22465
- scan_complete_at_resolve_time
22466
- };
23153
+ return cancelled_scope();
22467
23154
  }
22468
23155
  const merged_symbols = this.merge_chain(the_chain);
22469
23156
  let own_working_directory;
@@ -22497,6 +23184,9 @@ class ScopeResolver {
22497
23184
  max_depth: my_config.diagnostics?.max_depth
22498
23185
  }
22499
23186
  });
23187
+ if (token?.isCancellationRequested) {
23188
+ return cancelled_scope();
23189
+ }
22500
23190
  if (forward_result.call_sites.length > 0) {
22501
23191
  forward_call_symbols = forward_result.call_sites;
22502
23192
  }
@@ -22638,9 +23328,9 @@ class ScopeResolver {
22638
23328
  ...config,
22639
23329
  backward_dependencies: "explicit"
22640
23330
  };
22641
- return this.discover_working_directory(the_backward_directives, new Set, 0, my_config, new Map, current_uri);
23331
+ return this.discover_working_directory(the_backward_directives, new Set, 0, my_config, new Map, current_uri, undefined, true);
22642
23332
  }
22643
- async discover_working_directory(directives, visited, depth, config, request_cache, current_uri, token) {
23333
+ async discover_working_directory(directives, visited, depth, config, request_cache, current_uri, token, skip_backward_registration) {
22644
23334
  if (depth > config.max_backward_depth) {
22645
23335
  return;
22646
23336
  }
@@ -22661,7 +23351,8 @@ class ScopeResolver {
22661
23351
  try {
22662
23352
  my_parent_result = await this.get_parsed_file(my_parent_uri, my_real_path, {
22663
23353
  request_cache,
22664
- backward_dependencies: config.backward_dependencies ?? "auto"
23354
+ backward_dependencies: config.backward_dependencies ?? "auto",
23355
+ skip_backward_registration
22665
23356
  });
22666
23357
  } catch (error) {
22667
23358
  my_parent_result = { error: String(error) };
@@ -22691,7 +23382,7 @@ class ScopeResolver {
22691
23382
  } = this.get_effective_backward_directives(my_parent_uri, my_parent_result.directives, config, my_parent_result.is_standalone);
22692
23383
  if (effective_parent_directives.length > 0) {
22693
23384
  visited.add(my_parent_uri);
22694
- const my_deeper_wd = await this.discover_working_directory(effective_parent_directives, visited, depth + 1, config, request_cache, my_parent_uri, token);
23385
+ const my_deeper_wd = await this.discover_working_directory(effective_parent_directives, visited, depth + 1, config, request_cache, my_parent_uri, token, skip_backward_registration);
22695
23386
  visited.delete(my_parent_uri);
22696
23387
  if (my_deeper_wd) {
22697
23388
  return my_deeper_wd;
@@ -22930,8 +23621,9 @@ class ScopeResolver {
22930
23621
  }
22931
23622
  parse_file(uri, content) {
22932
23623
  const content_hash = this.hash_content(content);
22933
- const cached = this.file_cache.get(uri);
23624
+ const cached = this.file_cache.peek(uri);
22934
23625
  if (cached && cached.content_hash === content_hash) {
23626
+ this.file_cache.touch(uri);
22935
23627
  return {
22936
23628
  symbols: cached.symbols,
22937
23629
  directives: cached.directives,
@@ -22948,6 +23640,7 @@ class ScopeResolver {
22948
23640
  try {
22949
23641
  const my_parse_result = this.parse_content(uri, content);
22950
23642
  this.file_cache.set(uri, {
23643
+ uri,
22951
23644
  content,
22952
23645
  content_hash,
22953
23646
  size: Buffer.byteLength(content, "utf8"),
@@ -23088,11 +23781,14 @@ class ScopeResolver {
23088
23781
  async _get_parsed_file_impl(uri, fs_path, options) {
23089
23782
  const inherited_wd = options?.working_directory;
23090
23783
  const cache_key = this.make_file_cache_key(uri, inherited_wd);
23091
- const cached = this.file_cache.get(cache_key);
23784
+ const cached = this.file_cache.peek(cache_key);
23092
23785
  if (options?.skip_disk_if_cached && cached) {
23093
23786
  this.log(`[get_parsed_file] file_cache HIT for ${cache_key} (skip_disk_if_cached)`);
23094
23787
  this.cache_metrics.file.hits++;
23095
- this.upgrade_registration_on_cache_hit(uri, cached, options?.backward_dependencies);
23788
+ this.file_cache.touch(cache_key);
23789
+ if (!options?.skip_backward_registration) {
23790
+ this.upgrade_registration_on_cache_hit(uri, cached, options?.backward_dependencies);
23791
+ }
23096
23792
  return this.cache_entry_to_parsed_result(cached, cached.content, cached.content_hash);
23097
23793
  }
23098
23794
  let mtimeMs;
@@ -23104,7 +23800,10 @@ class ScopeResolver {
23104
23800
  if (cached && mtimeMs !== undefined && size !== undefined && cached.mtimeMs !== undefined && cached.mtimeMs === mtimeMs && cached.size !== undefined && cached.size === size) {
23105
23801
  this.cache_metrics.file.hits++;
23106
23802
  this.log(`[get_parsed_file] File cache HIT for ${uri} (mtime match, skipped read)`);
23107
- this.upgrade_registration_on_cache_hit(uri, cached, options?.backward_dependencies);
23803
+ this.file_cache.touch(cache_key);
23804
+ if (!options?.skip_backward_registration) {
23805
+ this.upgrade_registration_on_cache_hit(uri, cached, options?.backward_dependencies);
23806
+ }
23108
23807
  return this.cache_entry_to_parsed_result(cached, cached.content, cached.content_hash);
23109
23808
  }
23110
23809
  }
@@ -23122,11 +23821,14 @@ class ScopeResolver {
23122
23821
  const fallback_mtimeMs = fallback_stats?.mtimeMs;
23123
23822
  const fallback_size = fallback_stats?.size;
23124
23823
  const fallback_cache_key = this.make_file_cache_key(fallback_uri, inherited_wd);
23125
- const fallback_cached = this.file_cache.get(fallback_cache_key);
23824
+ const fallback_cached = this.file_cache.peek(fallback_cache_key);
23126
23825
  if (fallback_cached && fallback_mtimeMs !== undefined && fallback_size !== undefined && fallback_cached.mtimeMs !== undefined && fallback_cached.mtimeMs === fallback_mtimeMs && fallback_cached.size !== undefined && fallback_cached.size === fallback_size) {
23127
23826
  this.cache_metrics.file.hits++;
23128
23827
  this.log(`[get_parsed_file] File cache HIT for ${fallback_uri} (mtime match, skipped read)`);
23129
- this.upgrade_registration_on_cache_hit(fallback_uri, fallback_cached, options?.backward_dependencies);
23828
+ this.file_cache.touch(fallback_cache_key);
23829
+ if (!options?.skip_backward_registration) {
23830
+ this.upgrade_registration_on_cache_hit(fallback_uri, fallback_cached, options?.backward_dependencies);
23831
+ }
23130
23832
  return this.cache_entry_to_parsed_result(fallback_cached, fallback_cached.content, fallback_cached.content_hash);
23131
23833
  }
23132
23834
  }
@@ -23145,12 +23847,15 @@ class ScopeResolver {
23145
23847
  }
23146
23848
  }
23147
23849
  const actual_cache_key = this.make_file_cache_key(actual_uri, inherited_wd);
23148
- const actual_cached = this.file_cache.get(actual_cache_key);
23850
+ const actual_cached = this.file_cache.peek(actual_cache_key);
23149
23851
  const disk_hash = this.hash_content(content);
23150
23852
  if (actual_cached && actual_cached.content_hash === disk_hash) {
23151
23853
  this.cache_metrics.file.hits++;
23152
23854
  this.log(`[get_parsed_file] File cache HIT for ${actual_uri} (hash match)`);
23153
- this.upgrade_registration_on_cache_hit(actual_uri, actual_cached, options?.backward_dependencies);
23855
+ this.file_cache.touch(actual_cache_key);
23856
+ if (!options?.skip_backward_registration) {
23857
+ this.upgrade_registration_on_cache_hit(actual_uri, actual_cached, options?.backward_dependencies);
23858
+ }
23154
23859
  return this.cache_entry_to_parsed_result(actual_cached, content, disk_hash);
23155
23860
  } else if (actual_cached) {
23156
23861
  this.log(`[get_parsed_file] File cache STALE for ${actual_uri} (cached hash=${actual_cached.content_hash}, disk hash=${disk_hash})`);
@@ -23166,6 +23871,7 @@ class ScopeResolver {
23166
23871
  mtimeMs = stats?.mtimeMs;
23167
23872
  }
23168
23873
  this.file_cache.set(actual_cache_key, {
23874
+ uri: actual_uri,
23169
23875
  content,
23170
23876
  content_hash: disk_hash,
23171
23877
  mtimeMs,
@@ -23178,16 +23884,18 @@ class ScopeResolver {
23178
23884
  working_directory_directive: parse_result.working_directory_directive,
23179
23885
  is_standalone: parse_result.is_standalone,
23180
23886
  diagnostics: parse_result.diagnostics,
23181
- registered_backward_mode: options?.backward_dependencies ?? "explicit"
23887
+ registered_backward_mode: options?.skip_backward_registration ? undefined : options?.backward_dependencies ?? "explicit"
23182
23888
  });
23183
- this.apply_backward_directive_registration(actual_uri, parse_result.directives, { backward_dependencies: options?.backward_dependencies ?? "explicit" }, parse_result.is_standalone);
23889
+ if (!options?.skip_backward_registration) {
23890
+ this.apply_backward_directive_registration(actual_uri, parse_result.directives, { backward_dependencies: options?.backward_dependencies ?? "explicit" }, parse_result.is_standalone);
23891
+ }
23184
23892
  this.register_forward_call_relationships_from_cache(actual_uri, parse_result.forward_calls, parse_result.symbols);
23185
23893
  return { content, content_hash: disk_hash, ...parse_result };
23186
23894
  } catch (error) {
23187
23895
  this.warn(`ScopeResolver: Parse error for ${actual_uri}: ${error_message(error)}`);
23188
23896
  const empty_symbols = create_empty_symbol_table();
23189
23897
  const my_recovered = this.recover_header_facts(actual_uri, content);
23190
- if (my_recovered.recovered) {
23898
+ if (my_recovered.recovered && !options?.skip_backward_registration) {
23191
23899
  this.apply_backward_directive_registration(actual_uri, my_recovered.directives, {
23192
23900
  backward_dependencies: options?.backward_dependencies ?? "explicit"
23193
23901
  }, my_recovered.is_standalone);
@@ -23429,6 +24137,12 @@ class ScopeResolver {
23429
24137
  }
23430
24138
  };
23431
24139
  }
24140
+ get_cache_sizes() {
24141
+ return {
24142
+ scope: this.scope_cache.size,
24143
+ file: this.file_cache.size
24144
+ };
24145
+ }
23432
24146
  reset_cache_metrics() {
23433
24147
  this.cache_metrics = this.create_metrics();
23434
24148
  }
@@ -23633,14 +24347,7 @@ class ScopeResolver {
23633
24347
  }
23634
24348
  for (const my_key of keys_to_remove) {
23635
24349
  this.scope_cache.delete(my_key);
23636
- const key_uri = this.extract_uri_from_cache_key(my_key);
23637
- const key_set = this.uri_to_cache_keys.get(key_uri);
23638
- if (key_set) {
23639
- key_set.delete(my_key);
23640
- if (key_set.size === 0) {
23641
- this.uri_to_cache_keys.delete(key_uri);
23642
- }
23643
- }
24350
+ this.prune_uri_to_cache_keys_for_key(my_key);
23644
24351
  this.cache_metrics.scope.invalidations++;
23645
24352
  }
23646
24353
  }
@@ -23694,6 +24401,10 @@ class ScopeResolver {
23694
24401
  }
23695
24402
  upgrade_registration_on_cache_hit(uri, cached, requested_mode) {
23696
24403
  if (requested_mode !== "auto") {
24404
+ if (requested_mode === "explicit" && cached.registered_backward_mode === undefined) {
24405
+ this.apply_backward_directive_registration(uri, cached.directives, { backward_dependencies: "explicit" }, cached.is_standalone);
24406
+ cached.registered_backward_mode = "explicit";
24407
+ }
23697
24408
  return;
23698
24409
  }
23699
24410
  if (cached.directives.length > 0 && cached.registered_backward_mode === "auto") {
@@ -23857,7 +24568,7 @@ class ScopeResolver {
23857
24568
  this.reset_reverse_deps();
23858
24569
  }
23859
24570
  }
23860
- var DEFAULT_CONFIG2;
24571
+ var DEFAULT_CONFIG2, DEFAULT_MAX_CACHED_FILES = 2000, DEFAULT_MAX_CACHED_SCOPES = 1000;
23861
24572
  var init_scope_resolver = __esm(() => {
23862
24573
  init_esm();
23863
24574
  init_types();
@@ -23866,6 +24577,7 @@ var init_scope_resolver = __esm(() => {
23866
24577
  init_parser();
23867
24578
  init_analyzer();
23868
24579
  init_logger();
24580
+ init_lru_cache();
23869
24581
  init_dofile_locals();
23870
24582
  init_workspace_roots();
23871
24583
  init_file_path_utils();
@@ -27003,13 +27715,14 @@ class IndentationDiagnosticAnalyzer {
27003
27715
  const stataRanges = this.getStataRanges(document);
27004
27716
  const block_comment_lines2 = this.compute_block_comment_lines(lines, document.tokens);
27005
27717
  const continuation_lines = document.tokens ? this.compute_continuation_lines(document.tokens) : new Set;
27718
+ const structural_taint = this.compute_structural_taint(document);
27006
27719
  for (const range of stataRanges) {
27007
27720
  diagnostics.push(...this.find_comment_indentation_issues(lines, range, block_comment_lines2, indent_size));
27008
27721
  diagnostics.push(...this.find_block_indentation_issues(document, lines, range, block_comment_lines2, indent_size, continuation_lines));
27009
27722
  const expected_depths = this.compute_expected_depths(document, range);
27010
- diagnostics.push(...this.find_unnecessary_indentation_issues(document, lines, range, block_comment_lines2, indent_size, expected_depths, continuation_lines));
27723
+ diagnostics.push(...this.find_ast_depth_indentation_issues(lines, range, block_comment_lines2, indent_size, expected_depths, continuation_lines, structural_taint));
27011
27724
  }
27012
- return diagnostics;
27725
+ return this.dedupe_by_line_and_code(diagnostics);
27013
27726
  }
27014
27727
  compute_continuation_lines(tokens) {
27015
27728
  const continuation_lines = new Set;
@@ -27234,7 +27947,12 @@ class IndentationDiagnosticAnalyzer {
27234
27947
  const is_control_flow = node.type === "program" || node.type === "if" || node.type === "else" || node.type === "foreach" || node.type === "forvalues" || node.type === "while" || node.type === "frame";
27235
27948
  if (is_control_flow && node.body) {
27236
27949
  for (const child of node.body) {
27237
- walk_node(child, current_depth + 1);
27950
+ const child_start_line = child.range.start.line;
27951
+ if (child_start_line === start_line) {
27952
+ walk_node(child, current_depth);
27953
+ } else {
27954
+ walk_node(child, current_depth + 1);
27955
+ }
27238
27956
  }
27239
27957
  const end_line = node.range.end.line;
27240
27958
  if (end_line !== start_line && end_line >= range.start && end_line <= range.end) {
@@ -27277,7 +27995,7 @@ class IndentationDiagnosticAnalyzer {
27277
27995
  const block_node = node;
27278
27996
  for (const my_child of block_node.body) {
27279
27997
  const child_start_line = my_child.range.start.line;
27280
- if (child_start_line === start_line && this.is_block_node_type(my_child)) {
27998
+ if (child_start_line === start_line) {
27281
27999
  walk_node(my_child, depth);
27282
28000
  } else {
27283
28001
  walk_node(my_child, depth + 1);
@@ -27319,14 +28037,15 @@ class IndentationDiagnosticAnalyzer {
27319
28037
  }
27320
28038
  return false;
27321
28039
  }
27322
- find_unnecessary_indentation_issues(document, lines, range, block_comment_lines2, indent_size, expected_depths, continuation_lines) {
28040
+ find_ast_depth_indentation_issues(lines, range, block_comment_lines2, indent_size, expected_depths, continuation_lines, structural_taint) {
27323
28041
  const diagnostics = [];
27324
28042
  for (let i = range.start;i <= range.end && i < lines.length; i++) {
27325
28043
  const line = lines[i];
27326
- if (this.should_skip_unnecessary_check(line, i, block_comment_lines2, continuation_lines)) {
28044
+ if (this.should_skip_unnecessary_check(line, i, block_comment_lines2, continuation_lines) || structural_taint.has(i)) {
27327
28045
  continue;
27328
28046
  }
27329
28047
  const actual_indent = this.get_line_indentation(line, indent_size);
28048
+ const has_known_depth = expected_depths.has(i);
27330
28049
  const expected_depth = expected_depths.get(i) ?? 0;
27331
28050
  const expected_indent = expected_depth * indent_size;
27332
28051
  if (actual_indent > expected_indent) {
@@ -27338,12 +28057,94 @@ class IndentationDiagnosticAnalyzer {
27338
28057
  code: "UNNECESSARY_INDENTATION" /* UNNECESSARY_INDENTATION */,
27339
28058
  ...diagnostic_code_description_fields("UNNECESSARY_INDENTATION" /* UNNECESSARY_INDENTATION */)
27340
28059
  });
28060
+ } else if (has_known_depth && actual_indent < expected_indent) {
28061
+ diagnostics.push({
28062
+ severity: import_node.DiagnosticSeverity.Information,
28063
+ range: import_node.Range.create(import_node.Position.create(i, 0), import_node.Position.create(i, actual_indent)),
28064
+ message: "Line appears under-indented for its block depth. Use Format Document to fix.",
28065
+ source: "sight",
28066
+ code: "MISSING_INDENTATION" /* MISSING_INDENTATION */,
28067
+ ...diagnostic_code_description_fields("MISSING_INDENTATION" /* MISSING_INDENTATION */)
28068
+ });
27341
28069
  }
27342
28070
  }
27343
28071
  return diagnostics;
27344
28072
  }
28073
+ compute_structural_taint(document) {
28074
+ const tainted_lines = new Set;
28075
+ const the_error_lines = [];
28076
+ for (const my_diagnostic of document.diagnostics) {
28077
+ const my_code = typeof my_diagnostic.code === "string" ? my_diagnostic.code : "";
28078
+ if (STRUCTURAL_NESTING_ERROR_CODES.has(my_code)) {
28079
+ the_error_lines.push(my_diagnostic.range.start.line);
28080
+ }
28081
+ }
28082
+ if (the_error_lines.length === 0) {
28083
+ return tainted_lines;
28084
+ }
28085
+ if (!document.ast) {
28086
+ for (const my_error_line of the_error_lines) {
28087
+ tainted_lines.add(my_error_line);
28088
+ }
28089
+ return tainted_lines;
28090
+ }
28091
+ for (const my_error_line of the_error_lines) {
28092
+ const my_block = this.find_smallest_block_containing(document.ast, my_error_line);
28093
+ if (my_block) {
28094
+ for (let my_line = my_block.range.start.line;my_line <= my_block.range.end.line; my_line++) {
28095
+ tainted_lines.add(my_line);
28096
+ }
28097
+ } else {
28098
+ tainted_lines.add(my_error_line);
28099
+ }
28100
+ }
28101
+ return tainted_lines;
28102
+ }
28103
+ find_smallest_block_containing(ast, line) {
28104
+ let best = null;
28105
+ const walk = (node) => {
28106
+ const start_line = node.range.start.line;
28107
+ const end_line = node.range.end.line;
28108
+ if (line < start_line || line > end_line) {
28109
+ return;
28110
+ }
28111
+ if (this.is_indentation_block_node(node)) {
28112
+ if (best === null || end_line - start_line <= best.range.end.line - best.range.start.line) {
28113
+ best = node;
28114
+ }
28115
+ }
28116
+ if ("body" in node && Array.isArray(node.body)) {
28117
+ for (const my_child of node.body) {
28118
+ walk(my_child);
28119
+ }
28120
+ }
28121
+ };
28122
+ for (const my_node of ast.nodes) {
28123
+ walk(my_node);
28124
+ }
28125
+ return best;
28126
+ }
28127
+ is_indentation_block_node(node) {
28128
+ if (this.is_block_node_type(node)) {
28129
+ return true;
28130
+ }
28131
+ return node.type === "command" && "name" in node && node.name === "{";
28132
+ }
28133
+ dedupe_by_line_and_code(diagnostics) {
28134
+ const seen_line_codes = new Set;
28135
+ const deduped = [];
28136
+ for (const my_diagnostic of diagnostics) {
28137
+ const my_key = `${my_diagnostic.range.start.line}:${my_diagnostic.code}`;
28138
+ if (seen_line_codes.has(my_key)) {
28139
+ continue;
28140
+ }
28141
+ seen_line_codes.add(my_key);
28142
+ deduped.push(my_diagnostic);
28143
+ }
28144
+ return deduped;
28145
+ }
27345
28146
  }
27346
- var import_node, CONTROL_FLOW_RE, FIRST_WORD_RE;
28147
+ var import_node, CONTROL_FLOW_RE, FIRST_WORD_RE, STRUCTURAL_NESTING_ERROR_CODES;
27347
28148
  var init_indentation_diagnostics = __esm(() => {
27348
28149
  init_types2();
27349
28150
  init_types();
@@ -27351,6 +28152,17 @@ var init_indentation_diagnostics = __esm(() => {
27351
28152
  import_node = __toESM(require_main4(), 1);
27352
28153
  CONTROL_FLOW_RE = /^(if|foreach|while|program|mata|python)\b/;
27353
28154
  FIRST_WORD_RE = /^(\w+)\b/;
28155
+ STRUCTURAL_NESTING_ERROR_CODES = new Set([
28156
+ "OPEN_BRACE_ALONE",
28157
+ "ORPHAN_CLOSE_BRACE",
28158
+ "BRACE_NOT_ALONE",
28159
+ "BRACE_ELSE_SAME_LINE",
28160
+ "CODE_AFTER_OPEN_BRACE",
28161
+ "UNCLOSED_BLOCK",
28162
+ "MISSING_PROGRAM_END",
28163
+ "UNBALANCED_QUOTES",
28164
+ "UNBALANCED_BLOCK_COMMENT"
28165
+ ]);
27354
28166
  });
27355
28167
 
27356
28168
  // src/utils/diagnostic-severity.ts
@@ -28324,49 +29136,83 @@ var init_literal_macro_adjacency_diagnostics = __esm(() => {
28324
29136
 
28325
29137
  // src/providers/diagnostics-publish-gate.ts
28326
29138
  class DiagnosticsPublishGate {
28327
- last_published_version = new Map;
28328
- last_published_epoch = new Map;
28329
- current_epoch = new Map;
28330
- get_current_epoch(uri) {
28331
- return this.current_epoch.get(uri) ?? 0;
28332
- }
28333
- would_publish(uri, version, epoch) {
28334
- const my_last_version = this.last_published_version.get(uri);
28335
- if (my_last_version === undefined || version > my_last_version) {
28336
- return true;
29139
+ lifecycle_by_uri = new Map;
29140
+ next_lifecycle_epoch = 0;
29141
+ begin_lifecycle(uri, version) {
29142
+ const state = {
29143
+ lifecycle_epoch: this.next_lifecycle_epoch++,
29144
+ document_version: version,
29145
+ force_epoch: 0
29146
+ };
29147
+ this.lifecycle_by_uri.set(uri, state);
29148
+ return this.trigger_from(state);
29149
+ }
29150
+ trigger_for_validation(uri, version) {
29151
+ const state = this.lifecycle_by_uri.get(uri);
29152
+ if (!state) {
29153
+ return;
28337
29154
  }
28338
- if (version < my_last_version) {
28339
- return false;
29155
+ if (version > state.document_version) {
29156
+ state.document_version = version;
28340
29157
  }
28341
- const my_current_epoch = this.current_epoch.get(uri) ?? 0;
28342
- const my_last_epoch = this.last_published_epoch.get(uri) ?? 0;
28343
- return epoch === my_current_epoch && epoch > my_last_epoch;
29158
+ return {
29159
+ lifecycle_epoch: state.lifecycle_epoch,
29160
+ document_version: version,
29161
+ force_epoch: state.force_epoch
29162
+ };
28344
29163
  }
28345
- try_consume_publish(uri, version, epoch) {
28346
- const my_last_version = this.last_published_version.get(uri);
28347
- if (my_last_version === undefined || version > my_last_version) {
28348
- this.last_published_version.set(uri, version);
28349
- this.last_published_epoch.set(uri, epoch);
29164
+ is_current_trigger(uri, trigger) {
29165
+ return !!trigger && !!this.state_for_trigger(uri, trigger);
29166
+ }
29167
+ would_publish(uri, version, trigger) {
29168
+ const state = this.state_for_trigger(uri, trigger);
29169
+ if (!state || version !== trigger.document_version) {
29170
+ return false;
29171
+ }
29172
+ if (state.last_published_version === undefined || version > state.last_published_version) {
28350
29173
  return true;
28351
29174
  }
28352
- if (version < my_last_version) {
29175
+ if (version < state.last_published_version) {
28353
29176
  return false;
28354
29177
  }
28355
- const my_current_epoch = this.current_epoch.get(uri) ?? 0;
28356
- const my_last_epoch = this.last_published_epoch.get(uri) ?? 0;
28357
- if (epoch === my_current_epoch && epoch > my_last_epoch) {
28358
- this.last_published_epoch.set(uri, epoch);
28359
- return true;
29178
+ return trigger.force_epoch > (state.last_published_force_epoch ?? -1);
29179
+ }
29180
+ try_consume_publish(uri, version, trigger) {
29181
+ if (!this.would_publish(uri, version, trigger)) {
29182
+ return false;
28360
29183
  }
28361
- return false;
29184
+ const state = this.lifecycle_by_uri.get(uri);
29185
+ state.last_published_version = version;
29186
+ state.last_published_force_epoch = trigger.force_epoch;
29187
+ return true;
28362
29188
  }
28363
29189
  mark_force_republish(uri) {
28364
- this.current_epoch.set(uri, (this.current_epoch.get(uri) ?? 0) + 1);
29190
+ const state = this.lifecycle_by_uri.get(uri);
29191
+ if (!state) {
29192
+ return false;
29193
+ }
29194
+ state.force_epoch++;
29195
+ return true;
29196
+ }
29197
+ retire_lifecycle(uri) {
29198
+ this.lifecycle_by_uri.delete(uri);
28365
29199
  }
28366
- forget(uri) {
28367
- this.last_published_version.delete(uri);
28368
- this.last_published_epoch.delete(uri);
28369
- this.current_epoch.delete(uri);
29200
+ retire_all_lifecycles() {
29201
+ this.lifecycle_by_uri.clear();
29202
+ }
29203
+ trigger_from(state) {
29204
+ return {
29205
+ lifecycle_epoch: state.lifecycle_epoch,
29206
+ document_version: state.document_version,
29207
+ force_epoch: state.force_epoch
29208
+ };
29209
+ }
29210
+ state_for_trigger(uri, trigger) {
29211
+ const state = this.lifecycle_by_uri.get(uri);
29212
+ if (!state || state.lifecycle_epoch !== trigger.lifecycle_epoch || state.document_version !== trigger.document_version || state.force_epoch !== trigger.force_epoch) {
29213
+ return;
29214
+ }
29215
+ return state;
28370
29216
  }
28371
29217
  }
28372
29218
 
@@ -28404,6 +29250,7 @@ class DiagnosticsProvider {
28404
29250
  dependency_graph;
28405
29251
  publish_gate = new DiagnosticsPublishGate;
28406
29252
  filtered_cache = new Map;
29253
+ cache_generation = 0;
28407
29254
  constructor(connection, debounce_manager) {
28408
29255
  this.connection = connection;
28409
29256
  this.debounce_manager = debounce_manager || null;
@@ -28419,25 +29266,41 @@ class DiagnosticsProvider {
28419
29266
  set_debounce_manager(debounce_manager) {
28420
29267
  this.debounce_manager = debounce_manager;
28421
29268
  }
29269
+ on_document_opened(uri, version) {
29270
+ this.publish_gate.begin_lifecycle(uri, version);
29271
+ this.clear_cache_for_document(uri);
29272
+ }
29273
+ trigger_for_validation(uri, version) {
29274
+ return this.publish_gate.trigger_for_validation(uri, version);
29275
+ }
29276
+ is_validation_current(uri, trigger) {
29277
+ return this.publish_gate.is_current_trigger(uri, trigger);
29278
+ }
28422
29279
  mark_force_republish(uri) {
28423
29280
  this.publish_gate.mark_force_republish(uri);
28424
- this.filtered_cache.delete(uri);
29281
+ this.clear_cache_for_document(uri);
28425
29282
  }
28426
- async publish_diagnostics(document, config, workspace_symbols, scope_resolver, cancellation_token) {
28427
- const my_epoch = this.publish_gate.get_current_epoch(document.uri);
28428
- if (!this.publish_gate.would_publish(document.uri, document.version, my_epoch)) {
29283
+ async publish_diagnostics(document, config, diagnostics_trigger, workspace_symbols, scope_resolver, cancellation_token) {
29284
+ if (!this.publish_gate.would_publish(document.uri, document.version, diagnostics_trigger)) {
29285
+ return { diagnostics: [], pending: false };
29286
+ }
29287
+ const computation_token = polling_cancellation_token(() => this.publish_gate.is_current_trigger(document.uri, diagnostics_trigger), cancellation_token);
29288
+ if (computation_token.isCancellationRequested) {
28429
29289
  return { diagnostics: [], pending: false };
28430
29290
  }
28431
29291
  if (!config?.diagnostics?.enabled) {
28432
- if (!this.publish_gate.try_consume_publish(document.uri, document.version, my_epoch)) {
29292
+ if (!this.publish_gate.try_consume_publish(document.uri, document.version, diagnostics_trigger)) {
28433
29293
  return { diagnostics: [], pending: false };
28434
29294
  }
28435
29295
  this.clear_diagnostics(document.uri);
28436
29296
  return { diagnostics: [], pending: false };
28437
29297
  }
28438
29298
  const is_pending = this.debounce_manager?.is_pending(document.uri) ?? false;
28439
- const the_diagnostics = await this.get_diagnostics(document, config, workspace_symbols, scope_resolver, cancellation_token);
28440
- if (!this.publish_gate.try_consume_publish(document.uri, document.version, my_epoch)) {
29299
+ const the_diagnostics = await this.get_diagnostics(document, config, workspace_symbols, scope_resolver, computation_token);
29300
+ if (computation_token.isCancellationRequested) {
29301
+ return { diagnostics: [], pending: is_pending };
29302
+ }
29303
+ if (!this.publish_gate.try_consume_publish(document.uri, document.version, diagnostics_trigger)) {
28441
29304
  return { diagnostics: [], pending: is_pending };
28442
29305
  }
28443
29306
  this.connection.sendDiagnostics({
@@ -28447,7 +29310,10 @@ class DiagnosticsProvider {
28447
29310
  return { diagnostics: the_diagnostics, pending: is_pending };
28448
29311
  }
28449
29312
  async get_diagnostics(document, config, workspace_symbols, scope_resolver, cancellation_token) {
28450
- const cache_epoch_at_start = this.publish_gate.get_current_epoch(document.uri);
29313
+ if (cancellation_token?.isCancellationRequested) {
29314
+ return [];
29315
+ }
29316
+ const cache_generation_at_start = this.cache_generation;
28451
29317
  const config_hash = this.compute_config_hash(config);
28452
29318
  const uri_cache = this.filtered_cache.get(document.uri);
28453
29319
  const inner_key = `${document.version}:${config_hash}`;
@@ -28478,6 +29344,9 @@ class DiagnosticsProvider {
28478
29344
  }
28479
29345
  const my_resolve_config = scope_resolver_config_for(config);
28480
29346
  const resolved_scope = scope_resolver ? await scope_resolver.resolve(document.uri, document.content, my_resolve_config, cancellation_token) : undefined;
29347
+ if (cancellation_token?.isCancellationRequested) {
29348
+ return [];
29349
+ }
28481
29350
  const backward_dep_mode = config.cross_file?.backward_dependencies ?? "auto";
28482
29351
  const scan_complete_for_deferral = resolved_scope?.scan_complete_at_resolve_time ?? false;
28483
29352
  const defer_undefined_diagnostics = backward_dep_mode === "auto" && this.dependency_graph && !scan_complete_for_deferral && resolved_scope && !resolved_scope.has_directives && !resolved_scope.has_auto_parents && !resolved_scope.is_standalone;
@@ -28641,7 +29510,7 @@ class DiagnosticsProvider {
28641
29510
  }
28642
29511
  }
28643
29512
  }
28644
- if (this.publish_gate.get_current_epoch(document.uri) === cache_epoch_at_start) {
29513
+ if (this.cache_generation === cache_generation_at_start) {
28645
29514
  let cache_for_uri = this.filtered_cache.get(document.uri);
28646
29515
  if (!cache_for_uri) {
28647
29516
  cache_for_uri = new Map;
@@ -28723,12 +29592,21 @@ class DiagnosticsProvider {
28723
29592
  }
28724
29593
  clear_cache_for_document(uri) {
28725
29594
  this.filtered_cache.delete(uri);
29595
+ this.cache_generation++;
28726
29596
  }
28727
- on_document_closed(uri) {
28728
- this.publish_gate.forget(uri);
29597
+ on_document_hidden(uri) {
29598
+ this.publish_gate.retire_lifecycle(uri);
28729
29599
  this.clear_cache_for_document(uri);
28730
29600
  this.clear_diagnostics(uri);
28731
29601
  }
29602
+ on_document_closed(uri) {
29603
+ this.on_document_hidden(uri);
29604
+ }
29605
+ on_shutdown() {
29606
+ this.publish_gate.retire_all_lifecycles();
29607
+ this.filtered_cache.clear();
29608
+ this.cache_generation++;
29609
+ }
28732
29610
  convert_lexer_error(error, config) {
28733
29611
  let severity;
28734
29612
  switch (error.code) {
@@ -29930,137 +30808,6 @@ var init_command_database = __esm(() => {
29930
30808
  command_database = new CommandDatabase;
29931
30809
  });
29932
30810
 
29933
- // src/utils/lru-cache.ts
29934
- class LRUCache {
29935
- cache = new Map;
29936
- max_size;
29937
- hits = 0;
29938
- misses = 0;
29939
- evictions = 0;
29940
- current_generation = 0;
29941
- constructor(max_size = 100) {
29942
- this.max_size = max_size;
29943
- }
29944
- get(key) {
29945
- const entry = this.cache.get(key);
29946
- if (entry !== undefined) {
29947
- this.hits++;
29948
- entry.access_count++;
29949
- this.cache.delete(key);
29950
- this.cache.set(key, entry);
29951
- return entry.value;
29952
- } else {
29953
- this.misses++;
29954
- }
29955
- return;
29956
- }
29957
- set(key, value) {
29958
- this.cache.delete(key);
29959
- if (this.cache.size >= this.max_size) {
29960
- this.evict_one();
29961
- }
29962
- this.cache.set(key, {
29963
- value,
29964
- generation: this.current_generation,
29965
- access_count: 1
29966
- });
29967
- }
29968
- evict_one() {
29969
- let victim_key;
29970
- let victim_score = Infinity;
29971
- for (const [key, entry] of this.cache) {
29972
- const score = entry.generation * 1000 + entry.access_count;
29973
- if (score < victim_score) {
29974
- victim_score = score;
29975
- victim_key = key;
29976
- }
29977
- }
29978
- if (victim_key !== undefined) {
29979
- this.cache.delete(victim_key);
29980
- this.evictions++;
29981
- }
29982
- }
29983
- advance_generation() {
29984
- this.current_generation++;
29985
- }
29986
- trim_to_top_n(n) {
29987
- if (this.cache.size <= n)
29988
- return;
29989
- const entries = Array.from(this.cache.entries()).sort((a, b) => b[1].access_count - a[1].access_count);
29990
- this.cache.clear();
29991
- for (let i = 0;i < Math.min(n, entries.length); i++) {
29992
- this.cache.set(entries[i][0], entries[i][1]);
29993
- }
29994
- this.evictions += entries.length - n;
29995
- }
29996
- has(key) {
29997
- return this.cache.has(key);
29998
- }
29999
- clear() {
30000
- this.cache.clear();
30001
- }
30002
- size() {
30003
- return this.cache.size;
30004
- }
30005
- get_stats() {
30006
- return { hits: this.hits, misses: this.misses, evictions: this.evictions };
30007
- }
30008
- }
30009
-
30010
- class CompletionPrefixCache {
30011
- cache;
30012
- command_db_version = 0;
30013
- workspace_version = 0;
30014
- top_n_threshold;
30015
- constructor(max_size = 100, top_n_threshold = 200) {
30016
- this.cache = new LRUCache(max_size);
30017
- this.top_n_threshold = top_n_threshold;
30018
- }
30019
- make_key(prefix, context, document_version = 0) {
30020
- return `${context}:${prefix}:${this.command_db_version}:${this.workspace_version}:${document_version}`;
30021
- }
30022
- get_with_context(prefix, context, document_version = 0) {
30023
- return this.cache.get(this.make_key(prefix, context, document_version));
30024
- }
30025
- set_with_context(prefix, context, value, document_version = 0) {
30026
- const trimmed_value = this.apply_limit(value);
30027
- this.cache.set(this.make_key(prefix, context, document_version), trimmed_value);
30028
- return trimmed_value;
30029
- }
30030
- apply_limit(value) {
30031
- if (this.top_n_threshold > 0 && value.length > this.top_n_threshold) {
30032
- return value.slice(0, this.top_n_threshold);
30033
- }
30034
- return value;
30035
- }
30036
- has_with_context(prefix, context, document_version = 0) {
30037
- return this.cache.has(this.make_key(prefix, context, document_version));
30038
- }
30039
- invalidate_on_db_change(new_version) {
30040
- if (new_version !== this.command_db_version) {
30041
- this.command_db_version = new_version;
30042
- this.cache.clear();
30043
- this.cache.advance_generation();
30044
- }
30045
- }
30046
- invalidate_on_workspace_change(new_version) {
30047
- if (new_version !== this.workspace_version) {
30048
- this.workspace_version = new_version;
30049
- this.cache.clear();
30050
- this.cache.advance_generation();
30051
- }
30052
- }
30053
- clear() {
30054
- this.cache.clear();
30055
- }
30056
- size() {
30057
- return this.cache.size();
30058
- }
30059
- get_stats() {
30060
- return this.cache.get_stats();
30061
- }
30062
- }
30063
-
30064
30811
  // src/utils/symbol-index-cache.ts
30065
30812
  class SymbolIndexCache {
30066
30813
  filtered_views = new Map;
@@ -30429,12 +31176,58 @@ function compute_ranking_key(factors) {
30429
31176
  const name = factors.alphabetical_order.toLowerCase();
30430
31177
  return `${scope_priority}${directive_priority}${symbol_priority_padded}|${parent_uri}|${name}`;
30431
31178
  }
31179
+ function stata_region_floor_line(document, position) {
31180
+ const the_ranges = document.context_ranges;
31181
+ if (!the_ranges || the_ranges.length === 0) {
31182
+ return 0;
31183
+ }
31184
+ let floor_line = 0;
31185
+ for (const my_range of the_ranges) {
31186
+ if (my_range.context === "stata" /* STATA */) {
31187
+ continue;
31188
+ }
31189
+ if (my_range.start_delimiter?.command?.endsWith(":")) {
31190
+ continue;
31191
+ }
31192
+ if (my_range.range.end.line < position.line) {
31193
+ floor_line = Math.max(floor_line, my_range.range.end.line + 1);
31194
+ }
31195
+ }
31196
+ return floor_line;
31197
+ }
31198
+ function build_logical_text_before_cursor(document, position, tokens, start, physical_text_before_cursor) {
31199
+ if (start === undefined || tokens === undefined) {
31200
+ return physical_text_before_cursor;
31201
+ }
31202
+ const current_line = get_line_text(document, position.line);
31203
+ const physical_current = current_line.substring(start.line === position.line ? start.character : 0, position.character);
31204
+ if (start.line === position.line) {
31205
+ return physical_current;
31206
+ }
31207
+ const the_parts = [];
31208
+ for (let my_i = start.index;my_i < tokens.length && tokens[my_i].range.start.line < position.line; my_i++) {
31209
+ const my_token = tokens[my_i];
31210
+ if (LOGICAL_FLATTEN_SKIP_TYPES.has(my_token.type)) {
31211
+ continue;
31212
+ }
31213
+ the_parts.push(my_token.value);
31214
+ }
31215
+ const prior_text = the_parts.join(" ");
31216
+ return prior_text === "" ? physical_current : prior_text + " " + physical_current;
31217
+ }
30432
31218
  function detect_completion_context(document, position, tokens, command_db) {
30433
31219
  if (position.line >= get_line_count(document)) {
30434
31220
  return { type: "fallback" };
30435
31221
  }
30436
31222
  const current_line = get_line_text(document, position.line);
30437
31223
  const text_before_cursor = current_line.substring(0, position.character);
31224
+ let cached_logical_text;
31225
+ const get_logical_text = () => {
31226
+ if (cached_logical_text === undefined) {
31227
+ cached_logical_text = build_logical_text_before_cursor(document, position, tokens, logical_statement_start(tokens, position, stata_region_floor_line(document, position)), text_before_cursor);
31228
+ }
31229
+ return cached_logical_text;
31230
+ };
30438
31231
  const extended_macro_context = detect_extended_macro_context(text_before_cursor, position);
30439
31232
  if (extended_macro_context) {
30440
31233
  return extended_macro_context;
@@ -30443,7 +31236,7 @@ function detect_completion_context(document, position, tokens, command_db) {
30443
31236
  if (directive_context && !is_cursor_in_block_comment(document, position)) {
30444
31237
  return directive_context;
30445
31238
  }
30446
- const command_path_context = detect_command_path_context(text_before_cursor);
31239
+ const command_path_context = detect_command_path_context(get_logical_text());
30447
31240
  if (command_path_context) {
30448
31241
  return command_path_context;
30449
31242
  }
@@ -30451,18 +31244,18 @@ function detect_completion_context(document, position, tokens, command_db) {
30451
31244
  if (macro_context) {
30452
31245
  return macro_context;
30453
31246
  }
30454
- const option_context = detect_option_context(text_before_cursor, document, position);
31247
+ const option_context = detect_option_context(get_logical_text(), document, position);
30455
31248
  if (option_context) {
30456
31249
  return option_context;
30457
31250
  }
30458
- const subcommand_context = detect_subcommand_context(text_before_cursor, command_db);
31251
+ const subcommand_context = detect_subcommand_context(get_logical_text(), command_db);
30459
31252
  if (subcommand_context) {
30460
31253
  return subcommand_context;
30461
31254
  }
30462
- if (is_command_context(text_before_cursor)) {
31255
+ if (is_command_context(get_logical_text())) {
30463
31256
  return { type: "command" };
30464
31257
  }
30465
- if (is_variable_context(text_before_cursor)) {
31258
+ if (is_variable_context(get_logical_text())) {
30466
31259
  return { type: "variable" };
30467
31260
  }
30468
31261
  return { type: "fallback" };
@@ -30575,6 +31368,9 @@ function extract_command_name(text) {
30575
31368
  }
30576
31369
  const the_words = working_text.split(/\s+/).filter((w) => w.length > 0);
30577
31370
  for (const my_word of the_words) {
31371
+ if (my_word === ":") {
31372
+ continue;
31373
+ }
30578
31374
  const lower_word = my_word.toLowerCase();
30579
31375
  if (!PREFIX_COMMANDS2.includes(lower_word)) {
30580
31376
  return my_word;
@@ -30866,7 +31662,7 @@ class CompletionProvider {
30866
31662
  if (my_current_context === "stata" /* STATA */ && is_cursor_in_block_comment(document, position, { require_tokens: true })) {
30867
31663
  return [];
30868
31664
  }
30869
- const context = detect_completion_context(document, position, undefined, this.command_db);
31665
+ const context = detect_completion_context(document, position, my_current_context === "stata" /* STATA */ ? document.tokens : undefined, this.command_db);
30870
31666
  if (my_current_context !== "stata" /* STATA */ && context.type !== "macro") {
30871
31667
  const my_boundary_completions = this.get_block_boundary_completions(document, position);
30872
31668
  return my_boundary_completions;
@@ -31202,7 +31998,8 @@ class CompletionProvider {
31202
31998
  return "";
31203
31999
  }
31204
32000
  const current_line = get_line_text(document, position.line);
31205
- const text_before_cursor = current_line.substring(0, position.character);
32001
+ const physical_text_before_cursor = current_line.substring(0, position.character);
32002
+ const text_before_cursor = build_logical_text_before_cursor(document, position, document.tokens, logical_statement_start(document.tokens, position, stata_region_floor_line(document, position)), physical_text_before_cursor);
31206
32003
  let paren_depth = 0;
31207
32004
  let in_string = false;
31208
32005
  let last_comma_pos = -1;
@@ -32216,12 +33013,13 @@ class CompletionProvider {
32216
33013
  return position.line >= body_start && position.line < program.range.end.line;
32217
33014
  }
32218
33015
  }
32219
- var import_vscode_languageserver4, DIRECTIVE_PATH_CONTEXT_PATTERN;
33016
+ var import_vscode_languageserver4, DIRECTIVE_PATH_CONTEXT_PATTERN, LOGICAL_FLATTEN_SKIP_TYPES;
32220
33017
  var init_completion = __esm(() => {
32221
33018
  init_command_database();
32222
33019
  init_scoped_locals();
32223
33020
  init_dofile_locals();
32224
33021
  init_types2();
33022
+ init_lru_cache();
32225
33023
  init_scope_resolver();
32226
33024
  init_scope_resolver();
32227
33025
  init_analyzer();
@@ -32230,8 +33028,18 @@ var init_completion = __esm(() => {
32230
33028
  init_comment_utils();
32231
33029
  init_logger();
32232
33030
  init_macro_completion();
33031
+ init_statement_span();
32233
33032
  import_vscode_languageserver4 = __toESM(require_main4(), 1);
32234
33033
  DIRECTIVE_PATH_CONTEXT_PATTERN = new RegExp(`^\\s*(${DIRECTIVE_BODY_PREFIX_PATTERN}[a-zA-Z-]+)\\s*:?\\s*(.*)$`);
33034
+ LOGICAL_FLATTEN_SKIP_TYPES = new Set([
33035
+ "WHITESPACE",
33036
+ "COMMENT_LINE",
33037
+ "COMMENT_BLOCK",
33038
+ "CONTINUATION",
33039
+ "STATEMENT_TERMINATOR",
33040
+ "DELIMIT_DIRECTIVE",
33041
+ "EOF"
33042
+ ]);
32235
33043
  });
32236
33044
 
32237
33045
  // src/utils/marker-scanner.ts
@@ -32495,7 +33303,8 @@ function create_completion_handler(deps) {
32495
33303
  const config = await deps.get_document_settings(params.textDocument.uri);
32496
33304
  const graph_version = deps.dependency_graph ? deps.dependency_graph.get_version() : undefined;
32497
33305
  const items = await deps.completion_provider.get_completions(document_state, params.position, trigger_character, deps.scope_resolver || undefined, workspace_symbols, scope_resolver_config_for(config), workspace_version, token, graph_version);
32498
- const completion_context = detect_completion_context(document_state, params.position, document_state.tokens);
33306
+ const probe_context = document_state.context_tracker ? document_state.context_tracker.get_context_at_position(params.position) : "stata" /* STATA */;
33307
+ const completion_context = detect_completion_context(document_state, params.position, probe_context === "stata" /* STATA */ ? document_state.tokens : undefined);
32499
33308
  const is_macro_context = completion_context.type === "macro";
32500
33309
  return { isIncomplete: is_macro_context, items };
32501
33310
  }
@@ -32604,6 +33413,7 @@ function create_range_formatting_handler(deps) {
32604
33413
  }
32605
33414
  function create_shutdown_handler(deps, disposables) {
32606
33415
  return async () => {
33416
+ deps?.diagnostics_provider?.on_shutdown();
32607
33417
  deps?.workspace_indexer?.cancel();
32608
33418
  if (disposables?.pending_revalidations) {
32609
33419
  for (const my_token of disposables.pending_revalidations.values()) {
@@ -32853,6 +33663,7 @@ var import_node7, DEFAULT_SETTINGS, MAX_IHLP_CACHE_SIZE = 500, MAX_RESOLVE_STHLP
32853
33663
  var init_server_handlers = __esm(() => {
32854
33664
  init_completion();
32855
33665
  init_context_tracker();
33666
+ init_types2();
32856
33667
  init_scope_resolver();
32857
33668
  init_include_expander();
32858
33669
  init_marker_scanner();
@@ -32865,7 +33676,7 @@ var init_server_handlers = __esm(() => {
32865
33676
  severity: {
32866
33677
  undefinedMacro: "warning",
32867
33678
  undefinedVariable: "off",
32868
- styleWarnings: "hint",
33679
+ styleWarnings: "information",
32869
33680
  malformedOperator: "warning",
32870
33681
  spacedCompoundOperator: "information",
32871
33682
  invalidOperatorSequence: "error",
@@ -32906,6 +33717,9 @@ var init_server_handlers = __esm(() => {
32906
33717
  max_forward_depth: 10,
32907
33718
  max_chain_depth: 20,
32908
33719
  max_callee_revalidations: 10,
33720
+ max_cached_files: 2000,
33721
+ max_cached_scopes: 1000,
33722
+ max_cached_forward_closures: 2000,
32909
33723
  diagnostics: {
32910
33724
  missing_file: "warning",
32911
33725
  max_depth: "information",
@@ -33084,6 +33898,15 @@ function validate_comment_formatting_config(config, log_warning) {
33084
33898
  if (typeof cross_file.max_callee_revalidations === "number" && cross_file.max_callee_revalidations > 0) {
33085
33899
  validated_config.cross_file.max_callee_revalidations = cross_file.max_callee_revalidations;
33086
33900
  }
33901
+ if (typeof cross_file.max_cached_files === "number" && cross_file.max_cached_files > 0) {
33902
+ validated_config.cross_file.max_cached_files = cross_file.max_cached_files;
33903
+ }
33904
+ if (typeof cross_file.max_cached_scopes === "number" && cross_file.max_cached_scopes > 0) {
33905
+ validated_config.cross_file.max_cached_scopes = cross_file.max_cached_scopes;
33906
+ }
33907
+ if (typeof cross_file.max_cached_forward_closures === "number" && cross_file.max_cached_forward_closures > 0) {
33908
+ validated_config.cross_file.max_cached_forward_closures = cross_file.max_cached_forward_closures;
33909
+ }
33087
33910
  if (cross_file.diagnostics) {
33088
33911
  const valid_severities = ["error", "warning", "information", "info", "off"];
33089
33912
  const valid_case_mismatch_severities = [
@@ -33766,10 +34589,16 @@ async function build_check_context(workspace_root, config) {
33766
34589
  }
33767
34590
  },
33768
34591
  warn: (message) => console.error(message)
34592
+ }, undefined, {
34593
+ max_cached_files: config.cross_file.max_cached_files,
34594
+ max_cached_scopes: config.cross_file.max_cached_scopes
33769
34595
  });
33770
34596
  const forward_scope_resolver = new ForwardScopeResolver(scope_resolver, {
33771
34597
  max_forward_depth: config.cross_file.max_forward_depth
33772
34598
  });
34599
+ if (config.cross_file.max_cached_forward_closures !== undefined) {
34600
+ forward_scope_resolver.set_forward_closure_memo_capacity(config.cross_file.max_cached_forward_closures);
34601
+ }
33773
34602
  const diagnostics_provider = new DiagnosticsProvider({
33774
34603
  sendDiagnostics: () => {
33775
34604
  return;
@@ -34235,57 +35064,6 @@ var init_main = __esm(() => {
34235
35064
  })(TextDocument || (TextDocument = {}));
34236
35065
  });
34237
35066
 
34238
- // src/utils/token-utils.ts
34239
- function position_before2(a, b) {
34240
- return a.line < b.line || a.line === b.line && a.character < b.character;
34241
- }
34242
- function position_at_or_before(a, b) {
34243
- return a.line < b.line || a.line === b.line && a.character <= b.character;
34244
- }
34245
- function find_last_token_starting_at_or_before(tokens, position) {
34246
- let low = 0;
34247
- let high = tokens.length - 1;
34248
- let best = -1;
34249
- while (low <= high) {
34250
- const mid = low + high >>> 1;
34251
- const start = tokens[mid].range.start;
34252
- if (position_at_or_before(start, position)) {
34253
- best = mid;
34254
- low = mid + 1;
34255
- } else {
34256
- high = mid - 1;
34257
- }
34258
- }
34259
- return best;
34260
- }
34261
- function find_last_token_starting_before(tokens, position) {
34262
- let low = 0;
34263
- let high = tokens.length - 1;
34264
- let best = -1;
34265
- while (low <= high) {
34266
- const mid = low + high >>> 1;
34267
- const start = tokens[mid].range.start;
34268
- if (position_before2(start, position)) {
34269
- best = mid;
34270
- low = mid + 1;
34271
- } else {
34272
- high = mid - 1;
34273
- }
34274
- }
34275
- return best;
34276
- }
34277
- function find_token_index_at_position(tokens, position) {
34278
- const candidate = find_last_token_starting_at_or_before(tokens, position);
34279
- if (candidate === -1) {
34280
- return -1;
34281
- }
34282
- const end = tokens[candidate].range.end;
34283
- if (position_before2(position, end)) {
34284
- return candidate;
34285
- }
34286
- return -1;
34287
- }
34288
-
34289
35067
  // src/utils/string-literal-utils.ts
34290
35068
  function is_cursor_in_string_literal(document, position) {
34291
35069
  if (!document.tokens || document.tokens.length === 0) {
@@ -38481,27 +39259,20 @@ class IndentationAnalyzer {
38481
39259
  if (!this.has_trivia(node))
38482
39260
  return;
38483
39261
  const node_with_trivia = node;
38484
- if (node_with_trivia.leadingTrivia) {
38485
- for (const my_trivia of node_with_trivia.leadingTrivia) {
38486
- if (my_trivia.type === "comment") {
38487
- const trivia_line = my_trivia.range.start.line;
38488
- if (!this.indentation_map.has(trivia_line)) {
38489
- const { delta, original_indent } = this.calculate_indent_delta(trivia_line, this.current_depth);
38490
- this.set_indentation(trivia_line, this.current_depth, false, false, false, false, delta, original_indent);
38491
- }
38492
- }
38493
- }
38494
- }
38495
- if (node_with_trivia.trailingTrivia) {
38496
- for (const my_trivia of node_with_trivia.trailingTrivia) {
38497
- if (my_trivia.type === "comment") {
38498
- const trivia_line = my_trivia.range.start.line;
38499
- if (!this.indentation_map.has(trivia_line)) {
38500
- const { delta, original_indent } = this.calculate_indent_delta(trivia_line, this.current_depth);
38501
- this.set_indentation(trivia_line, this.current_depth, false, false, false, false, delta, original_indent);
38502
- }
38503
- }
38504
- }
39262
+ this.mark_comment_trivia_indentation(node_with_trivia.leadingTrivia);
39263
+ this.mark_comment_trivia_indentation(node_with_trivia.trailingTrivia);
39264
+ }
39265
+ mark_comment_trivia_indentation(the_trivia) {
39266
+ if (!the_trivia)
39267
+ return;
39268
+ for (const my_trivia of the_trivia) {
39269
+ if (my_trivia.type !== "comment")
39270
+ continue;
39271
+ const trivia_line = my_trivia.range.start.line;
39272
+ if (this.indentation_map.has(trivia_line))
39273
+ continue;
39274
+ const { delta, original_indent } = this.calculate_indent_delta(trivia_line, this.current_depth);
39275
+ this.set_indentation(trivia_line, this.current_depth, false, false, false, false, delta, original_indent);
38505
39276
  }
38506
39277
  }
38507
39278
  is_block_node(node) {
@@ -38524,6 +39295,7 @@ class IndentationAnalyzer {
38524
39295
  this.walk_node(my_child);
38525
39296
  }
38526
39297
  }
39298
+ this.process_block_ending_trivia(node);
38527
39299
  this.current_depth--;
38528
39300
  if (end_line !== start_line) {
38529
39301
  const end_delta_info = this.calculate_indent_delta(end_line, this.current_depth);
@@ -38556,6 +39328,7 @@ class IndentationAnalyzer {
38556
39328
  this.set_indentation(line, this.current_depth, false, false, false, false, delta, original_indent);
38557
39329
  }
38558
39330
  }
39331
+ this.process_block_ending_trivia(node);
38559
39332
  this.current_depth--;
38560
39333
  if (end_line !== start_line) {
38561
39334
  const end_delta_info = this.calculate_indent_delta(end_line, this.current_depth);
@@ -38567,6 +39340,10 @@ class IndentationAnalyzer {
38567
39340
  const { delta, original_indent } = this.calculate_indent_delta(start_line, this.current_depth);
38568
39341
  this.set_indentation(start_line, this.current_depth, false, false, false, false, delta, original_indent);
38569
39342
  }
39343
+ process_block_ending_trivia(node) {
39344
+ const node_with_trivia = node;
39345
+ this.mark_comment_trivia_indentation(node_with_trivia.blockEndingTrivia);
39346
+ }
38570
39347
  process_continuations(tokens) {
38571
39348
  let continuation_indent = 0;
38572
39349
  let in_continuation = false;
@@ -40070,6 +40847,7 @@ class PrettyPrinter {
40070
40847
  for (const my_stmt of node.body) {
40071
40848
  the_parts.push(this.printNode(my_stmt));
40072
40849
  }
40850
+ the_parts.push(this.printBlockEndingTrivia(node));
40073
40851
  this.current_indent--;
40074
40852
  the_parts.push(`${this.getIndent()}}`);
40075
40853
  }
@@ -40090,7 +40868,9 @@ class PrettyPrinter {
40090
40868
  printOption(option) {
40091
40869
  let result = option.name;
40092
40870
  if (option.argument !== undefined) {
40093
- result += `(${format_expression_spacing(option.argument)})`;
40871
+ const close_paren = option.argument_unclosed ? "" : ")";
40872
+ const argument = format_expression_spacing(option.argument);
40873
+ result += `(${argument}${close_paren}`;
40094
40874
  }
40095
40875
  return result;
40096
40876
  }
@@ -40102,6 +40882,7 @@ class PrettyPrinter {
40102
40882
  for (const my_stmt of node.body) {
40103
40883
  the_lines.push(this.printNode(my_stmt));
40104
40884
  }
40885
+ the_lines.push(this.printBlockEndingTrivia(node));
40105
40886
  this.current_indent--;
40106
40887
  the_lines.push(`${this.getIndent()}end`);
40107
40888
  return the_lines.join("");
@@ -40160,6 +40941,7 @@ class PrettyPrinter {
40160
40941
  for (const my_stmt of node.body) {
40161
40942
  the_lines.push(this.printNode(my_stmt));
40162
40943
  }
40944
+ the_lines.push(this.printBlockEndingTrivia(node));
40163
40945
  this.current_indent--;
40164
40946
  the_lines.push(`${this.getIndent()}}`);
40165
40947
  return the_lines.join("");
@@ -40220,6 +41002,19 @@ class PrettyPrinter {
40220
41002
  }
40221
41003
  return the_parts.join("");
40222
41004
  }
41005
+ printBlockEndingTrivia(node) {
41006
+ if (!node.blockEndingTrivia || node.blockEndingTrivia.length === 0) {
41007
+ return "";
41008
+ }
41009
+ const the_parts = [];
41010
+ for (const my_trivia of node.blockEndingTrivia) {
41011
+ the_parts.push(this.getIndent());
41012
+ the_parts.push(this.printTrivia(my_trivia));
41013
+ the_parts.push(`
41014
+ `);
41015
+ }
41016
+ return the_parts.join("");
41017
+ }
40223
41018
  printTrivia(trivia) {
40224
41019
  return trivia.content;
40225
41020
  }
@@ -40315,28 +41110,17 @@ class CodeFormatter {
40315
41110
  const the_embedded_blocks = new Map;
40316
41111
  let my_placeholder_counter = 0;
40317
41112
  let my_modified_content = document.content;
40318
- const the_sorted_ranges = [...context_ranges].sort((a, b) => b.range.start.line - a.range.start.line);
40319
- for (const my_range of the_sorted_ranges) {
41113
+ const the_replacement_units = this.coalesce_embedded_ranges(the_doc, context_ranges);
41114
+ for (const my_unit of the_replacement_units) {
40320
41115
  if (my_placeholder_counter >= MAX_EMBEDDED_BLOCKS) {
40321
41116
  break;
40322
41117
  }
40323
41118
  const my_placeholder = `__EMBEDDED_BLOCK_${my_placeholder_counter}__`;
40324
41119
  the_embedded_blocks.set(my_placeholder, {
40325
- content: this.extract_block_content(the_doc, my_range),
40326
- range: my_range
41120
+ content: my_unit.content,
41121
+ range: my_unit.range
40327
41122
  });
40328
- let actual_range;
40329
- if (my_range.is_single_line) {
40330
- actual_range = my_range.range;
40331
- } else {
40332
- const actual_end_line = my_range.end_delimiter ? my_range.end_delimiter.range.start.line : my_range.range.end.line;
40333
- const end_line_text = get_line_text(the_doc, actual_end_line);
40334
- actual_range = {
40335
- start: my_range.range.start,
40336
- end: { line: actual_end_line, character: end_line_text.length }
40337
- };
40338
- }
40339
- my_modified_content = this.replace_range_in_content(my_modified_content, actual_range, my_placeholder);
41123
+ my_modified_content = this.replace_range_in_content(my_modified_content, my_unit.actual_range, my_placeholder);
40340
41124
  my_placeholder_counter++;
40341
41125
  }
40342
41126
  const indent_size = options.tabSize ?? server_config?.formatting?.indentSize ?? 4;
@@ -40366,7 +41150,7 @@ class CodeFormatter {
40366
41150
  const indented_block_lines = block_lines.map((line, index) => {
40367
41151
  if (index === 0) {
40368
41152
  return leading_indent + line;
40369
- } else if (index === block_lines.length - 1 && line.trim() === expected_end_delimiter) {
41153
+ } else if (!my_block_info.range.is_single_line && index === block_lines.length - 1 && line.trim() === expected_end_delimiter) {
40370
41154
  return leading_indent + line;
40371
41155
  } else {
40372
41156
  return line;
@@ -40391,6 +41175,90 @@ class CodeFormatter {
40391
41175
  newText: this.strip_trailing_whitespace(my_formatted_content)
40392
41176
  }];
40393
41177
  }
41178
+ embedded_range_span(doc, context_range) {
41179
+ if (context_range.is_single_line) {
41180
+ return {
41181
+ start_line: context_range.range.start.line,
41182
+ end_line: context_range.range.end.line
41183
+ };
41184
+ }
41185
+ const my_end_line = context_range.end_delimiter ? context_range.end_delimiter.range.start.line : context_range.range.end.line;
41186
+ return { start_line: context_range.range.start.line, end_line: my_end_line };
41187
+ }
41188
+ coalesce_embedded_ranges(doc, context_ranges) {
41189
+ const the_spans = context_ranges.map((my_range) => ({
41190
+ my_range,
41191
+ span: this.embedded_range_span(doc, my_range)
41192
+ })).sort((a, b) => {
41193
+ if (a.span.start_line !== b.span.start_line) {
41194
+ return a.span.start_line - b.span.start_line;
41195
+ }
41196
+ return a.span.end_line - b.span.end_line;
41197
+ });
41198
+ const the_groups = [];
41199
+ for (const my_item of the_spans) {
41200
+ const my_last = the_groups[the_groups.length - 1];
41201
+ if (my_last && my_item.span.start_line <= my_last.end_line) {
41202
+ my_last.end_line = Math.max(my_last.end_line, my_item.span.end_line);
41203
+ my_last.members.push(my_item.my_range);
41204
+ } else {
41205
+ the_groups.push({
41206
+ start_line: my_item.span.start_line,
41207
+ end_line: my_item.span.end_line,
41208
+ members: [my_item.my_range]
41209
+ });
41210
+ }
41211
+ }
41212
+ const the_line_count = get_line_count(doc);
41213
+ const the_units = [];
41214
+ for (const my_group of the_groups) {
41215
+ if (my_group.members.length === 1) {
41216
+ const my_range = my_group.members[0];
41217
+ const my_end_line_text2 = get_line_text(doc, my_group.end_line);
41218
+ the_units.push({
41219
+ content: this.extract_block_content(doc, my_range),
41220
+ range: my_range,
41221
+ actual_range: {
41222
+ start: { line: my_group.start_line, character: 0 },
41223
+ end: {
41224
+ line: my_group.end_line,
41225
+ character: my_end_line_text2.length
41226
+ }
41227
+ }
41228
+ });
41229
+ continue;
41230
+ }
41231
+ const my_lines = [];
41232
+ for (let my_line = my_group.start_line;my_line <= my_group.end_line && my_line < the_line_count; my_line++) {
41233
+ const my_text = get_line_text(doc, my_line);
41234
+ my_lines.push(my_line === my_group.start_line ? my_text.trimStart() : my_text);
41235
+ }
41236
+ const my_end_line_text = get_line_text(doc, my_group.end_line);
41237
+ const my_synthetic_range = {
41238
+ context: my_group.members[0].context,
41239
+ range: {
41240
+ start: { line: my_group.start_line, character: 0 },
41241
+ end: { line: my_group.end_line, character: Number.MAX_SAFE_INTEGER }
41242
+ },
41243
+ start_delimiter: my_group.members[0].start_delimiter,
41244
+ is_single_line: true
41245
+ };
41246
+ the_units.push({
41247
+ content: my_lines.join(`
41248
+ `),
41249
+ range: my_synthetic_range,
41250
+ actual_range: {
41251
+ start: { line: my_group.start_line, character: 0 },
41252
+ end: {
41253
+ line: my_group.end_line,
41254
+ character: my_end_line_text.length
41255
+ }
41256
+ }
41257
+ });
41258
+ }
41259
+ the_units.sort((a, b) => b.actual_range.start.line - a.actual_range.start.line);
41260
+ return the_units;
41261
+ }
40394
41262
  extract_block_content(doc, context_range) {
40395
41263
  const the_start_line = context_range.range.start.line;
40396
41264
  const the_end_line = context_range.end_delimiter ? context_range.end_delimiter.range.start.line : context_range.range.end.line;
@@ -40767,6 +41635,64 @@ var init_file_rename_handler = __esm(() => {
40767
41635
  init_esm();
40768
41636
  });
40769
41637
 
41638
+ // src/diagnostic-resources.ts
41639
+ function as_record(value) {
41640
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : undefined;
41641
+ }
41642
+ function parse_diagnostic_uri_set(value) {
41643
+ if (!Array.isArray(value)) {
41644
+ return;
41645
+ }
41646
+ const uris = new Set;
41647
+ for (const candidate of value) {
41648
+ if (typeof candidate !== "string") {
41649
+ continue;
41650
+ }
41651
+ try {
41652
+ const parsed = URI.parse(candidate, true);
41653
+ if (parsed.scheme !== "") {
41654
+ uris.add(parsed.toString());
41655
+ }
41656
+ } catch {}
41657
+ }
41658
+ return uris;
41659
+ }
41660
+ function diagnostic_uris_from_initialization_options(options) {
41661
+ return parse_diagnostic_uri_set(as_record(options)?.["diagnosticUris"]);
41662
+ }
41663
+ function diagnostic_uris_from_notification(params) {
41664
+ return parse_diagnostic_uri_set(as_record(params)?.["diagnosticUris"]);
41665
+ }
41666
+ function same_diagnostic_uri_set(current, next) {
41667
+ if (current === undefined || current.size !== next.size) {
41668
+ return false;
41669
+ }
41670
+ for (const uri of current) {
41671
+ if (!next.has(uri)) {
41672
+ return false;
41673
+ }
41674
+ }
41675
+ return true;
41676
+ }
41677
+ function diagnostic_uri_set_changes(current, next) {
41678
+ const removed = [...current].filter((uri) => !next.has(uri));
41679
+ const added = [...next].filter((uri) => !current.has(uri));
41680
+ return { added, removed };
41681
+ }
41682
+ function settings_initialization_options(options) {
41683
+ const record = as_record(options);
41684
+ if (!record || !("diagnosticUris" in record) || "sight" in record) {
41685
+ return options;
41686
+ }
41687
+ const settings = { ...record };
41688
+ delete settings["diagnosticUris"];
41689
+ return settings;
41690
+ }
41691
+ var DIAGNOSTIC_RESOURCES_CHANGED_NOTIFICATION = "sight/diagnosticResourcesChanged";
41692
+ var init_diagnostic_resources = __esm(() => {
41693
+ init_esm();
41694
+ });
41695
+
40770
41696
  // src/command-database/caches/v18.json
40771
41697
  var v18_default;
40772
41698
  var init_v18 = __esm(() => {
@@ -71490,11 +72416,48 @@ async function create_server(options) {
71490
72416
  let last_applied_indexing_signature = "";
71491
72417
  let config_change_seq = 0;
71492
72418
  let init_options_config = undefined;
72419
+ let editor_diagnostic_uris = undefined;
72420
+ let shutdown_requested = false;
71493
72421
  let last_client_settings = undefined;
71494
72422
  let init_root_uri = null;
71495
72423
  const log_config_warning = (message) => {
71496
72424
  connection.console.log(`Configuration warning: ${message}`);
71497
72425
  };
72426
+ function diagnostics_publish_allowed(uri) {
72427
+ return editor_diagnostic_uris?.has(uri) ?? true;
72428
+ }
72429
+ function update_editor_diagnostic_uris(next) {
72430
+ if (same_diagnostic_uri_set(editor_diagnostic_uris, next)) {
72431
+ return;
72432
+ }
72433
+ const previous = editor_diagnostic_uris;
72434
+ editor_diagnostic_uris = next;
72435
+ if (!diagnostics_provider) {
72436
+ return;
72437
+ }
72438
+ if (previous === undefined) {
72439
+ for (const document of documents.all()) {
72440
+ if (!next.has(document.uri)) {
72441
+ diagnostics_provider.on_document_hidden(document.uri);
72442
+ }
72443
+ }
72444
+ return;
72445
+ }
72446
+ const changes = diagnostic_uri_set_changes(previous, next);
72447
+ for (const uri of changes.removed) {
72448
+ const document = documents.get(uri);
72449
+ if (document) {
72450
+ diagnostics_provider.on_document_hidden(document.uri);
72451
+ }
72452
+ }
72453
+ for (const uri of changes.added) {
72454
+ const document = documents.get(uri);
72455
+ if (document) {
72456
+ diagnostics_provider.on_document_opened(document.uri, document.version);
72457
+ validate_text_document(document);
72458
+ }
72459
+ }
72460
+ }
71498
72461
  function log_project_config_warnings(loaded) {
71499
72462
  for (const my_warning of loaded.warnings) {
71500
72463
  connection.console.log(`Project config warning: ${my_warning.message}`);
@@ -71745,6 +72708,13 @@ async function create_server(options) {
71745
72708
  function configure_completion_provider(settings) {
71746
72709
  completion_provider?.configure_completion(settings.completion);
71747
72710
  }
72711
+ function apply_cache_capacities(settings) {
72712
+ scope_resolver?.set_cache_capacities({
72713
+ max_cached_files: settings.cross_file?.max_cached_files ?? DEFAULT_SETTINGS.cross_file.max_cached_files,
72714
+ max_cached_scopes: settings.cross_file?.max_cached_scopes ?? DEFAULT_SETTINGS.cross_file.max_cached_scopes
72715
+ });
72716
+ forward_scope_resolver?.set_forward_closure_memo_capacity(settings.cross_file?.max_cached_forward_closures ?? DEFAULT_SETTINGS.cross_file.max_cached_forward_closures ?? 2000);
72717
+ }
71748
72718
  function reset_workspace_indexing_state() {
71749
72719
  workspace_indexer?.reset();
71750
72720
  dependency_graph?.reset();
@@ -71757,6 +72727,7 @@ async function create_server(options) {
71757
72727
  }
71758
72728
  last_applied_indexing_signature = indexing_affecting_signature(settings);
71759
72729
  configure_completion_provider(settings);
72730
+ apply_cache_capacities(settings);
71760
72731
  if (workspace_indexer) {
71761
72732
  workspace_indexer.configure(settings);
71762
72733
  workspace_indexer.set_max_indexed_files(settings.cross_file?.max_indexed_files ?? 1000);
@@ -71810,6 +72781,7 @@ async function create_server(options) {
71810
72781
  configure_workspace_indexing(settings, active_workspace_roots, true);
71811
72782
  } else {
71812
72783
  configure_completion_provider(settings);
72784
+ apply_cache_capacities(settings);
71813
72785
  workspace_indexer?.configure(settings);
71814
72786
  revalidate_all_open_docs();
71815
72787
  }
@@ -71897,15 +72869,35 @@ async function create_server(options) {
71897
72869
  const snapshot_uri = text_document.uri;
71898
72870
  const snapshot_version = text_document.version;
71899
72871
  const snapshot_content = text_document.getText();
72872
+ const diagnostics_trigger = diagnostics_provider?.trigger_for_validation(snapshot_uri, snapshot_version);
72873
+ const snapshot_is_current = () => documents.get(snapshot_uri) === text_document && text_document.version === snapshot_version;
72874
+ const validation_work_is_current = () => {
72875
+ if (shutdown_requested || !snapshot_is_current()) {
72876
+ return false;
72877
+ }
72878
+ if (!diagnostics_provider) {
72879
+ return true;
72880
+ }
72881
+ if (!diagnostics_publish_allowed(snapshot_uri)) {
72882
+ return true;
72883
+ }
72884
+ return diagnostics_trigger !== undefined && diagnostics_provider.is_validation_current(snapshot_uri, diagnostics_trigger);
72885
+ };
71900
72886
  debounce_manager.schedule_validation(snapshot_uri, snapshot_version, async () => {
71901
72887
  const settings = await get_document_settings(snapshot_uri);
72888
+ if (!validation_work_is_current()) {
72889
+ return;
72890
+ }
71902
72891
  const is_debug = settings.debug === true;
71903
72892
  const my_scope_resolver_config = scope_resolver_config_for(settings);
71904
72893
  const workspace_symbols = workspace_indexer ? workspace_indexer.get_all_symbols() : undefined;
71905
72894
  if (document_store.get(snapshot_uri)) {
71906
- await document_store.update(snapshot_uri, [{ text: snapshot_content }], snapshot_version, workspace_symbols, my_scope_resolver_config);
72895
+ await document_store.update(snapshot_uri, [{ text: snapshot_content }], snapshot_version, workspace_symbols, my_scope_resolver_config, validation_work_is_current);
71907
72896
  } else {
71908
- await document_store.open(snapshot_uri, snapshot_content, snapshot_version, workspace_symbols, my_scope_resolver_config);
72897
+ await document_store.open(snapshot_uri, snapshot_content, snapshot_version, workspace_symbols, my_scope_resolver_config, validation_work_is_current);
72898
+ }
72899
+ if (!validation_work_is_current()) {
72900
+ return;
71909
72901
  }
71910
72902
  if (scope_resolver) {
71911
72903
  const document_state2 = document_store.get(snapshot_uri);
@@ -71964,14 +72956,20 @@ ${scope_resolver.get_reverse_deps_debug_info()}`);
71964
72956
  }
71965
72957
  }
71966
72958
  }
72959
+ if (!snapshot_is_current() || !diagnostics_publish_allowed(snapshot_uri)) {
72960
+ return;
72961
+ }
71967
72962
  const document_state = document_store.get(snapshot_uri);
71968
72963
  if (!document_state) {
71969
72964
  connection.sendDiagnostics({ uri: snapshot_uri, diagnostics: [] });
71970
72965
  return;
71971
72966
  }
71972
72967
  if (diagnostics_provider) {
72968
+ if (!diagnostics_trigger) {
72969
+ return;
72970
+ }
71973
72971
  const diag_workspace_symbols = workspace_indexer ? workspace_indexer.get_all_symbols() : undefined;
71974
- const result = await diagnostics_provider.publish_diagnostics(document_state, settings, diag_workspace_symbols, scope_resolver || undefined, undefined);
72972
+ const result = await diagnostics_provider.publish_diagnostics(document_state, settings, diagnostics_trigger, diag_workspace_symbols, scope_resolver || undefined, undefined);
71975
72973
  if (result.pending) {
71976
72974
  if (is_debug) {
71977
72975
  connection.console.log(`Diagnostics pending for ${snapshot_uri}`);
@@ -71992,7 +72990,8 @@ ${scope_resolver.get_reverse_deps_debug_info()}`);
71992
72990
  connection.onInitialize(create_initialize_handler((caps) => {
71993
72991
  server_capabilities = caps;
71994
72992
  }, (options2) => {
71995
- init_options_config = options2;
72993
+ editor_diagnostic_uris = diagnostic_uris_from_initialization_options(options2);
72994
+ init_options_config = settings_initialization_options(options2);
71996
72995
  }, (root_uri) => {
71997
72996
  init_root_uri = root_uri;
71998
72997
  }));
@@ -72125,6 +73124,7 @@ ${scope_resolver.get_reverse_deps_debug_info()}`);
72125
73124
  configure_workspace_indexing(settings, active_workspace_roots, true);
72126
73125
  } else {
72127
73126
  configure_completion_provider(settings);
73127
+ apply_cache_capacities(settings);
72128
73128
  workspace_indexer?.configure(settings);
72129
73129
  revalidate_all_open_docs();
72130
73130
  }
@@ -72148,6 +73148,9 @@ ${scope_resolver.get_reverse_deps_debug_info()}`);
72148
73148
  }
72149
73149
  });
72150
73150
  documents.onDidOpen((e) => {
73151
+ if (diagnostics_publish_allowed(e.document.uri)) {
73152
+ diagnostics_provider?.on_document_opened(e.document.uri, e.document.version);
73153
+ }
72151
73154
  validate_text_document(e.document);
72152
73155
  });
72153
73156
  documents.onDidClose((e) => {
@@ -72215,6 +73218,12 @@ ${scope_resolver.get_reverse_deps_debug_info()}`);
72215
73218
  }, async () => {
72216
73219
  await reload_project_config_from_active_root();
72217
73220
  }));
73221
+ connection.onNotification(DIAGNOSTIC_RESOURCES_CHANGED_NOTIFICATION, (params) => {
73222
+ const next = diagnostic_uris_from_notification(params);
73223
+ if (next !== undefined) {
73224
+ update_editor_diagnostic_uris(next);
73225
+ }
73226
+ });
72218
73227
  const completion_handler = create_completion_handler(handler_deps);
72219
73228
  const hover_handler = create_hover_handler(handler_deps);
72220
73229
  const definition_handler = create_definition_handler(handler_deps);
@@ -72241,7 +73250,10 @@ ${scope_resolver.get_reverse_deps_debug_info()}`);
72241
73250
  connection.onExecuteCommand((params) => {
72242
73251
  return execute_command_handler(params.command, params.arguments || []);
72243
73252
  });
72244
- connection.onShutdown(shutdown_handler);
73253
+ connection.onShutdown(() => {
73254
+ shutdown_requested = true;
73255
+ return shutdown_handler();
73256
+ });
72245
73257
  connection.onExit(create_exit_handler());
72246
73258
  connection.onRequest("sight/getWorkingDirectory", working_directory_handler);
72247
73259
  const shared_ihlp = create_shared_ihlp_resolver(handler_deps);
@@ -72278,6 +73290,7 @@ var init_server_factory = __esm(() => {
72278
73290
  init_dependency_graph();
72279
73291
  init_esm();
72280
73292
  init_stata_install_paths();
73293
+ init_diagnostic_resources();
72281
73294
  init_v18();
72282
73295
  init_server_handlers();
72283
73296
  import_node8 = __toESM(require_main4(), 1);
@@ -72472,4 +73485,4 @@ export {
72472
73485
  is_cli_entry_point
72473
73486
  };
72474
73487
 
72475
- //# debugId=5FE144BA73C2961164756E2164756E21
73488
+ //# debugId=5DA4F84D00AA02FC64756E2164756E21