simdref 0.0.18__tar.gz → 0.0.19__tar.gz

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.
Files changed (179) hide show
  1. {simdref-0.0.18/src/simdref.egg-info → simdref-0.0.19}/PKG-INFO +1 -1
  2. {simdref-0.0.18 → simdref-0.0.19}/pyproject.toml +1 -1
  3. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/asmline.py +132 -12
  4. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/lsp.py +125 -12
  5. {simdref-0.0.18 → simdref-0.0.19/src/simdref.egg-info}/PKG-INFO +1 -1
  6. {simdref-0.0.18 → simdref-0.0.19}/tests/test_lsp_ptx.py +19 -0
  7. {simdref-0.0.18 → simdref-0.0.19}/tests/test_lsp_stdio.py +212 -3
  8. {simdref-0.0.18 → simdref-0.0.19}/LICENSE +0 -0
  9. {simdref-0.0.18 → simdref-0.0.19}/README.md +0 -0
  10. {simdref-0.0.18 → simdref-0.0.19}/setup.cfg +0 -0
  11. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/__init__.py +0 -0
  12. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/__main__.py +0 -0
  13. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/annotate.py +0 -0
  14. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/arm_instructions.py +0 -0
  15. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/asm_identity.py +0 -0
  16. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/catkey.py +0 -0
  17. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/cli.py +0 -0
  18. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/cuda.py +0 -0
  19. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/display.py +0 -0
  20. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/export.py +0 -0
  21. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/filters.py +0 -0
  22. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/htmltext.py +0 -0
  23. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/ingest.py +0 -0
  24. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/ingest_catalog.py +0 -0
  25. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/ingest_pdf.py +0 -0
  26. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/ingest_sources.py +0 -0
  27. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/manpages.py +0 -0
  28. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/models.py +0 -0
  29. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/operands.py +0 -0
  30. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/pdfparse/__init__.py +0 -0
  31. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/pdfparse/base.py +0 -0
  32. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/pdfparse/intel.py +0 -0
  33. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/pdfparse/registry.py +0 -0
  34. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/pdfparse/types.py +0 -0
  35. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/pdfrefs.py +0 -0
  36. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf.py +0 -0
  37. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf_sources/__init__.py +0 -0
  38. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf_sources/amd_swopt.py +0 -0
  39. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf_sources/arm_swopt.py +0 -0
  40. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf_sources/cores.py +0 -0
  41. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf_sources/instlatx64.py +0 -0
  42. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf_sources/intel_guide.py +0 -0
  43. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf_sources/intel_optref.py +0 -0
  44. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf_sources/llvm_mca.py +0 -0
  45. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf_sources/llvm_scheduling.py +0 -0
  46. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf_sources/merge.py +0 -0
  47. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/ptx.py +0 -0
  48. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/queries.py +0 -0
  49. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/regtable.py +0 -0
  50. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/riscv.py +0 -0
  51. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/search.py +0 -0
  52. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/storage.py +0 -0
  53. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/tablecols.py +0 -0
  54. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/tui.py +0 -0
  55. {simdref-0.0.18 → simdref-0.0.19}/src/simdref/ui_labels.py +0 -0
  56. {simdref-0.0.18 → simdref-0.0.19}/src/simdref.egg-info/SOURCES.txt +0 -0
  57. {simdref-0.0.18 → simdref-0.0.19}/src/simdref.egg-info/dependency_links.txt +0 -0
  58. {simdref-0.0.18 → simdref-0.0.19}/src/simdref.egg-info/entry_points.txt +0 -0
  59. {simdref-0.0.18 → simdref-0.0.19}/src/simdref.egg-info/requires.txt +0 -0
  60. {simdref-0.0.18 → simdref-0.0.19}/src/simdref.egg-info/top_level.txt +0 -0
  61. {simdref-0.0.18 → simdref-0.0.19}/tests/test_a1_sdm_parser.py +0 -0
  62. {simdref-0.0.18 → simdref-0.0.19}/tests/test_a2_perf_rule.py +0 -0
  63. {simdref-0.0.18 → simdref-0.0.19}/tests/test_a4_arm_pdfrefs.py +0 -0
  64. {simdref-0.0.18 → simdref-0.0.19}/tests/test_a5_offline_aarchmrs.py +0 -0
  65. {simdref-0.0.18 → simdref-0.0.19}/tests/test_amd_match_llvm_mc.py +0 -0
  66. {simdref-0.0.18 → simdref-0.0.19}/tests/test_annotate.py +0 -0
  67. {simdref-0.0.18 → simdref-0.0.19}/tests/test_annotate_positions.py +0 -0
  68. {simdref-0.0.18 → simdref-0.0.19}/tests/test_annotate_scans.py +0 -0
  69. {simdref-0.0.18 → simdref-0.0.19}/tests/test_arrangement_tail.py +0 -0
  70. {simdref-0.0.18 → simdref-0.0.19}/tests/test_asm_identity.py +0 -0
  71. {simdref-0.0.18 → simdref-0.0.19}/tests/test_asm_wire.py +0 -0
  72. {simdref-0.0.18 → simdref-0.0.19}/tests/test_asmline.py +0 -0
  73. {simdref-0.0.18 → simdref-0.0.19}/tests/test_audit_coverage.py +0 -0
  74. {simdref-0.0.18 → simdref-0.0.19}/tests/test_auto_update_on_version_change.py +0 -0
  75. {simdref-0.0.18 → simdref-0.0.19}/tests/test_b1_latency_pairs.py +0 -0
  76. {simdref-0.0.18 → simdref-0.0.19}/tests/test_b2_metadata.py +0 -0
  77. {simdref-0.0.18 → simdref-0.0.19}/tests/test_b3_sdm_forms.py +0 -0
  78. {simdref-0.0.18 → simdref-0.0.19}/tests/test_b4_descriptions_provenance.py +0 -0
  79. {simdref-0.0.18 → simdref-0.0.19}/tests/test_b5_linking.py +0 -0
  80. {simdref-0.0.18 → simdref-0.0.19}/tests/test_b5_perf_family.py +0 -0
  81. {simdref-0.0.18 → simdref-0.0.19}/tests/test_b5_sme.py +0 -0
  82. {simdref-0.0.18 → simdref-0.0.19}/tests/test_c0_perf_priority.py +0 -0
  83. {simdref-0.0.18 → simdref-0.0.19}/tests/test_catkey.py +0 -0
  84. {simdref-0.0.18 → simdref-0.0.19}/tests/test_cli_bare_query.py +0 -0
  85. {simdref-0.0.18 → simdref-0.0.19}/tests/test_cli_bootstrap_progress.py +0 -0
  86. {simdref-0.0.18 → simdref-0.0.19}/tests/test_cli_help.py +0 -0
  87. {simdref-0.0.18 → simdref-0.0.19}/tests/test_cli_llm.py +0 -0
  88. {simdref-0.0.18 → simdref-0.0.19}/tests/test_coverage_live.py +0 -0
  89. {simdref-0.0.18 → simdref-0.0.19}/tests/test_coverage_parity.py +0 -0
  90. {simdref-0.0.18 → simdref-0.0.19}/tests/test_cuda_declaration_parser.py +0 -0
  91. {simdref-0.0.18 → simdref-0.0.19}/tests/test_cuda_intrinsics.py +0 -0
  92. {simdref-0.0.18 → simdref-0.0.19}/tests/test_cuda_td_expansion.py +0 -0
  93. {simdref-0.0.18 → simdref-0.0.19}/tests/test_cuda_wiring.py +0 -0
  94. {simdref-0.0.18 → simdref-0.0.19}/tests/test_display.py +0 -0
  95. {simdref-0.0.18 → simdref-0.0.19}/tests/test_display_scanners.py +0 -0
  96. {simdref-0.0.18 → simdref-0.0.19}/tests/test_export.py +0 -0
  97. {simdref-0.0.18 → simdref-0.0.19}/tests/test_export_viz.py +0 -0
  98. {simdref-0.0.18 → simdref-0.0.19}/tests/test_fetch_retry.py +0 -0
  99. {simdref-0.0.18 → simdref-0.0.19}/tests/test_filters.py +0 -0
  100. {simdref-0.0.18 → simdref-0.0.19}/tests/test_fixui_display.py +0 -0
  101. {simdref-0.0.18 → simdref-0.0.19}/tests/test_htmltext.py +0 -0
  102. {simdref-0.0.18 → simdref-0.0.19}/tests/test_ingest_enc_fields.py +0 -0
  103. {simdref-0.0.18 → simdref-0.0.19}/tests/test_ingest_sources.py +0 -0
  104. {simdref-0.0.18 → simdref-0.0.19}/tests/test_int_arm_isa_label.py +0 -0
  105. {simdref-0.0.18 → simdref-0.0.19}/tests/test_int_core_ids.py +0 -0
  106. {simdref-0.0.18 → simdref-0.0.19}/tests/test_int_ingest_registration.py +0 -0
  107. {simdref-0.0.18 → simdref-0.0.19}/tests/test_int_sdm_family_fallback.py +0 -0
  108. {simdref-0.0.18 → simdref-0.0.19}/tests/test_intel_operation_and_url.py +0 -0
  109. {simdref-0.0.18 → simdref-0.0.19}/tests/test_isa_detect_llvm_mc.py +0 -0
  110. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue2_fixes.py +0 -0
  111. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_51.py +0 -0
  112. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_52.py +0 -0
  113. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_53_54.py +0 -0
  114. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_55.py +0 -0
  115. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_56_57a.py +0 -0
  116. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_57_cpi.py +0 -0
  117. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_60_c1.py +0 -0
  118. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_60_contract.py +0 -0
  119. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_60_crossarch.py +0 -0
  120. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_60_matcher.py +0 -0
  121. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_arm_alias.py +0 -0
  122. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_branch_target.py +0 -0
  123. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_compressed.py +0 -0
  124. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_foreign_arch.py +0 -0
  125. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_identity.py +0 -0
  126. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_m1.py +0 -0
  127. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_m5.py +0 -0
  128. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_perfdata.py +0 -0
  129. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_sme_za.py +0 -0
  130. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_text.py +0 -0
  131. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_timing.py +0 -0
  132. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issues_23_24_25.py +0 -0
  133. {simdref-0.0.18 → simdref-0.0.19}/tests/test_issues_51_57_join.py +0 -0
  134. {simdref-0.0.18 → simdref-0.0.19}/tests/test_leftover_arm_form_pick.py +0 -0
  135. {simdref-0.0.18 → simdref-0.0.19}/tests/test_leftover_arm_postindex.py +0 -0
  136. {simdref-0.0.18 → simdref-0.0.19}/tests/test_leftover_att_movx.py +0 -0
  137. {simdref-0.0.18 → simdref-0.0.19}/tests/test_leftover_autoupdate.py +0 -0
  138. {simdref-0.0.18 → simdref-0.0.19}/tests/test_leftover_catalog_gaps.py +0 -0
  139. {simdref-0.0.18 → simdref-0.0.19}/tests/test_leftover_ingest_isa_merge.py +0 -0
  140. {simdref-0.0.18 → simdref-0.0.19}/tests/test_leftover_intel_size.py +0 -0
  141. {simdref-0.0.18 → simdref-0.0.19}/tests/test_leftover_perf_join.py +0 -0
  142. {simdref-0.0.18 → simdref-0.0.19}/tests/test_leftover_riscv_shared.py +0 -0
  143. {simdref-0.0.18 → simdref-0.0.19}/tests/test_lsp_cuda.py +0 -0
  144. {simdref-0.0.18 → simdref-0.0.19}/tests/test_lsp_web.py +0 -0
  145. {simdref-0.0.18 → simdref-0.0.19}/tests/test_models.py +0 -0
  146. {simdref-0.0.18 → simdref-0.0.19}/tests/test_no_catalog_row.py +0 -0
  147. {simdref-0.0.18 → simdref-0.0.19}/tests/test_objdump_listings.py +0 -0
  148. {simdref-0.0.18 → simdref-0.0.19}/tests/test_operands.py +0 -0
  149. {simdref-0.0.18 → simdref-0.0.19}/tests/test_pdfparse.py +0 -0
  150. {simdref-0.0.18 → simdref-0.0.19}/tests/test_perf.py +0 -0
  151. {simdref-0.0.18 → simdref-0.0.19}/tests/test_perf_source_amd_swopt.py +0 -0
  152. {simdref-0.0.18 → simdref-0.0.19}/tests/test_perf_source_arm_swopt.py +0 -0
  153. {simdref-0.0.18 → simdref-0.0.19}/tests/test_perf_source_instlatx64.py +0 -0
  154. {simdref-0.0.18 → simdref-0.0.19}/tests/test_perf_source_intel_guide.py +0 -0
  155. {simdref-0.0.18 → simdref-0.0.19}/tests/test_perf_source_intel_optref.py +0 -0
  156. {simdref-0.0.18 → simdref-0.0.19}/tests/test_perf_sources.py +0 -0
  157. {simdref-0.0.18 → simdref-0.0.19}/tests/test_presentation.py +0 -0
  158. {simdref-0.0.18 → simdref-0.0.19}/tests/test_preset_persistence.py +0 -0
  159. {simdref-0.0.18 → simdref-0.0.19}/tests/test_ptx.py +0 -0
  160. {simdref-0.0.18 → simdref-0.0.19}/tests/test_regtable.py +0 -0
  161. {simdref-0.0.18 → simdref-0.0.19}/tests/test_release_tag_pin.py +0 -0
  162. {simdref-0.0.18 → simdref-0.0.19}/tests/test_riscv_docs_pages.py +0 -0
  163. {simdref-0.0.18 → simdref-0.0.19}/tests/test_search.py +0 -0
  164. {simdref-0.0.18 → simdref-0.0.19}/tests/test_search_pushdown.py +0 -0
  165. {simdref-0.0.18 → simdref-0.0.19}/tests/test_shared_source_helpers.py +0 -0
  166. {simdref-0.0.18 → simdref-0.0.19}/tests/test_source_kind_filter.py +0 -0
  167. {simdref-0.0.18 → simdref-0.0.19}/tests/test_source_validation.py +0 -0
  168. {simdref-0.0.18 → simdref-0.0.19}/tests/test_storage_alnum.py +0 -0
  169. {simdref-0.0.18 → simdref-0.0.19}/tests/test_storage_payload.py +0 -0
  170. {simdref-0.0.18 → simdref-0.0.19}/tests/test_storage_schema.py +0 -0
  171. {simdref-0.0.18 → simdref-0.0.19}/tests/test_tablecols.py +0 -0
  172. {simdref-0.0.18 → simdref-0.0.19}/tests/test_tui.py +0 -0
  173. {simdref-0.0.18 → simdref-0.0.19}/tests/test_ui_labels_parity.py +0 -0
  174. {simdref-0.0.18 → simdref-0.0.19}/tests/test_update_build_stamp.py +0 -0
  175. {simdref-0.0.18 → simdref-0.0.19}/tests/test_vendor_riscv_categories.py +0 -0
  176. {simdref-0.0.18 → simdref-0.0.19}/tests/test_vendor_riscv_sources.py +0 -0
  177. {simdref-0.0.18 → simdref-0.0.19}/tests/test_version_matches_pyproject.py +0 -0
  178. {simdref-0.0.18 → simdref-0.0.19}/tests/test_x86_key_widths.py +0 -0
  179. {simdref-0.0.18 → simdref-0.0.19}/tests/test_x86_linking.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simdref
3
- Version: 0.0.18
3
+ Version: 0.0.19
4
4
  Summary: Searchable SIMD intrinsic and instruction reference with CLI, manpages, TUI, and LSP.
5
5
  Author: Marco
6
6
  License: GPL-3.0-or-later
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "simdref"
7
- version = "0.0.18"
7
+ version = "0.0.19"
8
8
  description = "Searchable SIMD intrinsic and instruction reference with CLI, manpages, TUI, and LSP."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -174,28 +174,96 @@ def c_asm_zone_at(text: str, offset: int) -> str | None:
174
174
 
175
175
  'template' is an asm(...) template string, 'msvc' an MSVC __asm block
176
176
  or line, 'operands' the operand and clobber lists after the first ':'
177
- of an asm(...) call. None is plain code. Shares _asm_call_at with
177
+ of an asm(...) call, 'comment' a C comment, 'string' a string literal
178
+ that is no asm template. None is plain code. Shares _asm_call_at with
178
179
  c_asm_literals, so hover and completion parse the same calls.
179
180
  """
181
+ span = c_span_at(text, offset)
182
+ if span == "comment":
183
+ return span
184
+ zone = c_asm_zone_span_at(text, offset)
185
+ return zone[0] if zone is not None else span
186
+
187
+
188
+ def c_asm_zone_span_at(text: str, offset: int) -> tuple[str, int, int] | None:
189
+ """(kind, low, high) of the asm zone holding the offset, or None.
190
+
191
+ The same walk as c_asm_zone_at. lsp.py reads the bounds: each MSVC
192
+ __asm statement starts at its own zone's low bound.
193
+ """
180
194
  masked = _mask_c_comments(text)
181
195
  index = 0
182
196
  while index < len(masked):
183
197
  hit = _asm_call_at(masked, index)
184
198
  if hit is not None:
185
199
  block_end, literals, zones = hit
186
- for kind, low, high in zones:
187
- if low <= offset <= high:
188
- return kind
189
- for start, _body in literals:
190
- if start <= offset <= start + len(_body):
191
- return "template"
200
+ for zone in zones:
201
+ if zone[1] <= offset <= zone[2]:
202
+ return zone
203
+ for start, body in literals:
204
+ if start <= offset <= start + len(body):
205
+ return "template", start, start + len(body)
192
206
  index = block_end
193
207
  else:
194
208
  index += 1
195
209
  return None
196
210
 
197
211
 
212
+ def c_span_at(text: str, offset: int) -> str | None:
213
+ """'comment' or 'string' when a cursor offset sits inside one, else None.
214
+
215
+ Same scan as _mask_c_comments. A cursor right after the opening
216
+ delimiter is inside; a cursor after the closing one is outside. An
217
+ unterminated block comment runs to the end, an unterminated string to
218
+ the end of its line: both are still being typed. A char literal and a
219
+ raw string are their own tokens: the double quote inside the char
220
+ literal '"' never opens a string, and a raw string keeps its quotes.
221
+ """
222
+ index = 0
223
+ while index < offset:
224
+ line_end = text.find("\n", index)
225
+ line_end = len(text) if line_end == -1 else line_end
226
+ if text[index] == "'":
227
+ end = _char_end(text, index) or line_end + 1
228
+ if index < offset < end:
229
+ return "string"
230
+ elif text[index] == '"':
231
+ end = _raw_string_end(text, index)
232
+ if end is None:
233
+ end = _string_end(text, index) or line_end + 1
234
+ if index < offset < end:
235
+ return "string"
236
+ elif text.startswith("//", index):
237
+ end = line_end
238
+ if index + 2 <= offset <= end:
239
+ return "comment"
240
+ elif text.startswith("/*", index):
241
+ close = text.find("*/", index + 2)
242
+ end = len(text) if close == -1 else close + 2
243
+ if index + 2 <= offset <= (end if close == -1 else close):
244
+ return "comment"
245
+ else:
246
+ index += 1
247
+ continue
248
+ index = max(end, index + 1)
249
+ return None
250
+
251
+
198
252
  _QUALIFIERS = ("volatile", "__volatile__", "goto", "inline")
253
+ _ASM_KEYWORDS = ("__asm__", "__asm", "asm")
254
+
255
+
256
+ def _asm_keyword_at(text: str, index: int) -> str | None:
257
+ """The asm keyword at index with word boundaries on both sides, or None."""
258
+ if index > 0 and (text[index - 1].isalnum() or text[index - 1] == "_"):
259
+ return None
260
+ for keyword in _ASM_KEYWORDS:
261
+ if text.startswith(keyword, index):
262
+ after = index + len(keyword)
263
+ tail = text[after : after + 1]
264
+ if not (tail.isalnum() or tail == "_"):
265
+ return keyword
266
+ return None
199
267
 
200
268
 
201
269
  def _asm_call_at(
@@ -214,7 +282,7 @@ def _asm_call_at(
214
282
  and (text[index - 1].isalnum() or text[index - 1] == "_")
215
283
  ):
216
284
  return None
217
- for keyword in ("__asm__", "__asm", "asm"):
285
+ for keyword in _ASM_KEYWORDS:
218
286
  if text.startswith(keyword, index):
219
287
  break
220
288
  else:
@@ -240,9 +308,12 @@ def _asm_call_at(
240
308
  return None
241
309
  return close + 1, [], [("msvc", cursor + 1, close)]
242
310
  if text[cursor : cursor + 1] != "(":
243
- # MSVC __asm <insn> line: the instruction runs to end of line.
244
- end = index
311
+ # MSVC __asm <insn>: the statement runs to end of line, or to the
312
+ # next __asm keyword, which starts its own statement on the row.
313
+ end = cursor
245
314
  while end < len(text) and text[end] != "\n":
315
+ if _asm_keyword_at(text, end):
316
+ break
246
317
  end += 1
247
318
  return end, [], [("msvc", cursor, end)]
248
319
  cursor = _skip_space(text, cursor + 1)
@@ -353,14 +424,63 @@ def _string_end(text: str, index: int, spans_lines: bool = False) -> int | None:
353
424
  return None
354
425
 
355
426
 
427
+ def _char_end(text: str, index: int) -> int | None:
428
+ """The index after the closing quote of the char literal at index, or None.
429
+
430
+ A char literal is one token like a string: its quote never opens a
431
+ string, and an escape pair ("\\'", "\\"") counts as one character.
432
+ """
433
+ if text[index : index + 1] != "'":
434
+ return None
435
+ cursor = index + 1
436
+ while cursor < len(text):
437
+ char = text[cursor]
438
+ if char == "\\":
439
+ cursor += 2
440
+ elif char == "'":
441
+ return cursor + 1
442
+ elif char == "\n":
443
+ return None
444
+ else:
445
+ cursor += 1
446
+ return None
447
+
448
+
449
+ def _raw_string_end(text: str, index: int) -> int | None:
450
+ """The index after the R"delim(...)delim" raw string whose quote is at index.
451
+
452
+ None when the shape is not a raw string: the caller then reads the
453
+ quote as a plain string. An unterminated raw string runs one past the
454
+ end of the text, because its body spans lines.
455
+ """
456
+ if text[index - 1 : index] != "R":
457
+ return None
458
+ open_paren = text.find("(", index + 1, index + 18)
459
+ if open_paren == -1:
460
+ return None
461
+ delim = text[index + 1 : open_paren]
462
+ if len(delim) > 16 or any(char.isspace() or char in "()\\" for char in delim):
463
+ return None
464
+ close = text.find(")" + delim + '"', open_paren + 1)
465
+ return len(text) + 1 if close == -1 else close + len(delim) + 2
466
+
467
+
356
468
  def _mask_c_comments(text: str) -> str:
357
- """Blank C comments to spaces (newlines stay). String literals stay."""
469
+ """Blank C comments to spaces (newlines stay). Literal tokens stay."""
358
470
  out: list[str] = []
359
471
  index = 0
360
472
  while index < len(text):
361
473
  char = text[index]
474
+ if char == "'":
475
+ end = _char_end(text, index)
476
+ if end is not None:
477
+ out.append(text[index:end])
478
+ index = end
479
+ continue
362
480
  if char == '"':
363
- end = _string_end(text, index)
481
+ end = _raw_string_end(text, index)
482
+ if end is None:
483
+ end = _string_end(text, index)
364
484
  if end is not None:
365
485
  out.append(text[index:end])
366
486
  index = end
@@ -1420,24 +1420,126 @@ def _completion_candidates(
1420
1420
  return items
1421
1421
 
1422
1422
 
1423
+ def _at_mnemonic(before: str, semicolon_is_comment: bool, ptx: bool) -> bool:
1424
+ """True when the cursor ends the first word of an asm statement.
1425
+
1426
+ before is the text from the line start (or the template start) to the
1427
+ cursor. A label, a PTX predicate and the x86 prefix words may come
1428
+ first. A cursor inside a comment is not at a mnemonic.
1429
+ """
1430
+ from simdref.annotate import _strip_prefix_tokens # annotate imports lsp
1431
+
1432
+ mark = "\0" # a comment cut removes it, so the cursor is then in a comment
1433
+ probe = asmline.cut_asm_comment(before + mark, _hash_opens_operand)
1434
+ if semicolon_is_comment:
1435
+ probe = asmline.cut_semicolon(probe)
1436
+ else:
1437
+ low, high = asmline.statement_spans(probe)[-1]
1438
+ probe = probe[low:high]
1439
+ rest = probe.lstrip()
1440
+ while True:
1441
+ parts = rest.split(None, 1)
1442
+ if len(parts) == 2 and (parts[0].endswith(":") or (ptx and parts[0].startswith("@"))):
1443
+ rest = parts[1]
1444
+ else:
1445
+ break
1446
+ word = _strip_prefix_tokens(rest).removesuffix(mark)
1447
+ return rest.endswith(mark) and (not word or asmline.word_at(word, len(word)) == word)
1448
+
1449
+
1450
+ def _msvc_before(text: str, offset: int) -> str:
1451
+ """The MSVC __asm statement text before the cursor.
1452
+
1453
+ Each __asm keyword and each row inside a brace block starts a new
1454
+ statement. The asm zone marks the keyword case at its low bound; the
1455
+ row start bounds the brace case.
1456
+ """
1457
+ start = text.rfind("\n", 0, offset) + 1
1458
+ zone = asmline.c_asm_zone_span_at(text, offset)
1459
+ if zone is not None:
1460
+ start = max(start, zone[1])
1461
+ return text[start:offset]
1462
+
1463
+
1464
+ def _objdump_mnemonic_column(line: str) -> int | None:
1465
+ """Start column of the mnemonic on an objdump address line, else None.
1466
+
1467
+ Reuses annotate's objdump line split: its rest starts at the mnemonic,
1468
+ after the address and byte columns.
1469
+ """
1470
+ from simdref.annotate import _parse_objdump_instr # annotate imports lsp
1471
+
1472
+ parsed = _parse_objdump_instr(line)
1473
+ if parsed is None:
1474
+ return None
1475
+ return len(line.rstrip()) - len(parsed[1])
1476
+
1477
+
1478
+ def _template_before(text: str, offset: int) -> str | None:
1479
+ """The asm statement text before the cursor inside an asm template string."""
1480
+ for start, body in asmline.c_asm_literals(text):
1481
+ if start <= offset <= start + len(body):
1482
+ spaced = body[: offset - start].replace("\\t", " ")
1483
+ return spaced[asmline.statement_spans(spaced)[-1][0] :]
1484
+ return None
1485
+
1486
+
1423
1487
  def _completion_items(
1424
- conn, text: str, language_id: str, uri: str, prefix: str, line: int, character: int
1488
+ conn,
1489
+ text: str,
1490
+ language_id: str,
1491
+ uri: str,
1492
+ prefix: str,
1493
+ line: int,
1494
+ character: int,
1495
+ trigger_kind: int | None = None,
1496
+ trigger_char: str | None = None,
1425
1497
  ) -> list[dict]:
1426
1498
  """Completion items for one cursor position.
1427
1499
 
1428
- Assembly documents keep the full pool. In C and C++ the pool follows
1429
- the cursor: mnemonics only inside an asm template string, an MSVC
1430
- __asm block or an MSVC __asm line; no simdref items in the operand and
1431
- clobber lists; intrinsics only everywhere else.
1500
+ Completion answers only where the item kind can be written. Nothing
1501
+ inside a comment or a plain string, nothing for an empty prefix unless
1502
+ the user invoked completion by hand (trigger kind 1), nothing from a
1503
+ "." trigger outside PTX names. Assembly and PTX documents offer items
1504
+ at the mnemonic position of a statement only. In C and C++ the pool
1505
+ follows the cursor: mnemonics only inside an asm template string, an
1506
+ MSVC __asm block or an MSVC __asm line; no simdref items in the operand
1507
+ and clobber lists; intrinsics only everywhere else.
1432
1508
  """
1509
+ if not prefix and trigger_kind != 1:
1510
+ return []
1433
1511
  ptx = _is_ptx_doc(language_id, uri) or _is_cuda_doc(language_id, uri)
1434
1512
  cuda = _is_cuda_doc(language_id, uri)
1435
- if _is_ptx_doc(language_id, uri):
1436
- return _completion_candidates(conn, prefix, kinds=_MNEMONIC_KINDS, ptx=True, cuda=cuda)
1437
1513
  if not _is_c_doc(language_id, uri):
1438
- return _completion_candidates(conn, prefix, ptx=ptx, cuda=cuda)
1439
- zone = asmline.c_asm_zone_at(text, _offset_at(text, line, character))
1440
- if zone == "operands":
1514
+ is_ptx = _is_ptx_doc(language_id, uri)
1515
+ if trigger_char == "." and not is_ptx:
1516
+ return []
1517
+ lines = text.split("\n")
1518
+ source_line = lines[line].rstrip("\r") if line < len(lines) else ""
1519
+ before = source_line[: _code_point_index(source_line, character)]
1520
+ cut = _objdump_mnemonic_column(source_line)
1521
+ if cut is not None:
1522
+ # An objdump address row names its mnemonic after the address
1523
+ # and byte columns; an item fits at that column only.
1524
+ if len(before) < cut:
1525
+ return []
1526
+ before = before[cut:]
1527
+ if not _at_mnemonic(before, _semicolon_is_comment(language_id, uri), is_ptx):
1528
+ return []
1529
+ return _completion_candidates(
1530
+ conn, prefix, kinds=_MNEMONIC_KINDS if is_ptx else None, ptx=ptx, cuda=cuda
1531
+ )
1532
+ offset = _offset_at(text, line, character)
1533
+ zone = asmline.c_asm_zone_at(text, offset)
1534
+ if zone in ("operands", "comment", "string"):
1535
+ return []
1536
+ if trigger_char == "." and not (cuda and zone == "template"):
1537
+ return []
1538
+ if zone == "template":
1539
+ before = _template_before(text, offset)
1540
+ if before is None or not _at_mnemonic(before, False, cuda):
1541
+ return []
1542
+ elif zone == "msvc" and not _at_mnemonic(_msvc_before(text, offset), True, False):
1441
1543
  return []
1442
1544
  kinds = _MNEMONIC_KINDS if zone in ("template", "msvc") else _INTRINSIC_KINDS
1443
1545
  return _completion_candidates(conn, prefix, kinds=kinds, ptx=ptx, cuda=cuda)
@@ -1738,7 +1840,7 @@ def main() -> int:
1738
1840
  "inlayHintProvider": True,
1739
1841
  "completionProvider": {
1740
1842
  "resolveProvider": False,
1741
- "triggerCharacters": ["_", ".", "m", "v"],
1843
+ "triggerCharacters": ["_", "."],
1742
1844
  },
1743
1845
  }
1744
1846
  },
@@ -1852,8 +1954,19 @@ def main() -> int:
1852
1954
  language_id = session.languages.get(uri, "")
1853
1955
  line, character = params["position"]["line"], params["position"]["character"]
1854
1956
  prefix = _line_prefix(text, line, character)
1957
+ context = params.get("context") or {}
1855
1958
  items = (
1856
- _completion_items(conn, text, language_id, uri, prefix, line, character)
1959
+ _completion_items(
1960
+ conn,
1961
+ text,
1962
+ language_id,
1963
+ uri,
1964
+ prefix,
1965
+ line,
1966
+ character,
1967
+ context.get("triggerKind"),
1968
+ context.get("triggerCharacter"),
1969
+ )
1857
1970
  if conn
1858
1971
  else []
1859
1972
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simdref
3
- Version: 0.0.18
3
+ Version: 0.0.19
4
4
  Summary: Searchable SIMD intrinsic and instruction reference with CLI, manpages, TUI, and LSP.
5
5
  Author: Marco
6
6
  License: GPL-3.0-or-later
@@ -239,6 +239,25 @@ class LspPtxTests(unittest.TestCase):
239
239
  self.assertEqual(kinds, {3})
240
240
  self.assertNotIn("frob d, [a]", [item["label"] for item in items])
241
241
 
242
+ def test_completion_ptx_dotted_name_still_completes(self):
243
+ # A hand-typed "frob." and a "." trigger both complete in a .ptx file.
244
+ text = "frob.\n"
245
+ self.assertTrue(self.server.completion(PTX_URI, "ptx", text, 0, 5))
246
+ msg = self.server.request(
247
+ "textDocument/completion",
248
+ {
249
+ "textDocument": {"uri": PTX_URI},
250
+ "position": {"line": 0, "character": 5},
251
+ "context": {"triggerKind": 2, "triggerCharacter": "."},
252
+ },
253
+ )
254
+ self.assertTrue(msg["result"]["items"])
255
+
256
+ def test_completion_ptx_operand_and_comment_give_no_items(self):
257
+ text = "frob.f32 %f1, frob\n// frob comment\n"
258
+ self.assertEqual(self.server.completion(PTX_URI, "ptx", text, 0, 18), [])
259
+ self.assertEqual(self.server.completion(PTX_URI, "ptx", text, 1, 7), [])
260
+
242
261
  def test_completion_dot_prefix_unchanged_outside_ptx(self):
243
262
  # Control: a dotted prefix in a .s document keeps origin/main's
244
263
  # empty answer. The dot rule is ptx-only (review finding 1).
@@ -1038,12 +1038,20 @@ C_COMPLETION_INTRINSICS = [
1038
1038
  "<arm_neon.h>",
1039
1039
  "arm",
1040
1040
  ),
1041
+ _c_intrinsic(
1042
+ "_mm256_cmpeq_epi8",
1043
+ "__m256i _mm256_cmpeq_epi8 (__m256i a, __m256i b)",
1044
+ "Compare packed 8-bit integers.",
1045
+ "<immintrin.h>",
1046
+ "x86",
1047
+ ),
1041
1048
  ]
1042
1049
  C_COMPLETION_INSTRUCTIONS = [
1043
1050
  _c_insn("vaddps", "VADDPS (XMM, XMM, XMM)", "Add packed single precision values."),
1044
1051
  _c_insn("vmulps", "VMULPS (XMM, XMM, XMM)", "Multiply packed single precision values."),
1045
1052
  _c_insn("vmovaps", "VMOVAPS (XMM, XMM)", "Move aligned packed single precision values."),
1046
1053
  _c_insn("mov", "MOV (R64, R64)", "Move."),
1054
+ _c_insn("cbw", "CBW (X86)", "Convert byte to word."),
1047
1055
  ]
1048
1056
 
1049
1057
  # One document per zone. Lines: 3-4 multi-line asm template, 5 asm with the
@@ -1159,10 +1167,9 @@ class LspCCompletionTests(unittest.TestCase):
1159
1167
  self.assertEqual(self._kinds(items), {3})
1160
1168
  self.assertEqual(self._labels(items), ["vaddq_f32"])
1161
1169
 
1162
- def test_comment_gives_intrinsics_only(self):
1170
+ def test_comment_gives_no_items(self):
1163
1171
  items = self.server.completion(C_COMPLETION_URI, 12, self._col(12, "vaddps", 2))
1164
- self.assertEqual(self._kinds(items), {3})
1165
- self.assertEqual(self._labels(items), ["vaddq_f32"])
1172
+ self.assertEqual(items, [])
1166
1173
 
1167
1174
  def test_plain_string_gives_no_mnemonics(self):
1168
1175
  items = self.server.completion(C_COMPLETION_URI, 11, self._col(11, "mov", 1))
@@ -1184,6 +1191,208 @@ class LspCCompletionTests(unittest.TestCase):
1184
1191
  )
1185
1192
 
1186
1193
 
1194
+ SCOPE_C_URI = "file:///tmp/simdref_lsp_scope.c"
1195
+ SCOPE_C_TEXT = (
1196
+ "// vaddps comment\n"
1197
+ "/* _mm */\n"
1198
+ 'char *s = "_mm";\n'
1199
+ "int y = foo.\n"
1200
+ "_mm_add\n"
1201
+ 'void g(void) { asm("vadd %%xmm0, %%xmm1\\n\\tmov %%eax, vadd"); }\n'
1202
+ )
1203
+ SCOPE_ASM_URI = "file:///tmp/simdref_lsp_scope.s"
1204
+ SCOPE_ASM_TEXT = "# vadd comment\nvadd ymm0, vadd\n vadd\nloop: vadd\n"
1205
+ SCOPE_NASM_URI = "file:///tmp/simdref_lsp_scope.asm"
1206
+ SCOPE_NASM_TEXT = "; vadd comment\nvadd ymm0, ymm1 ; vadd\n"
1207
+
1208
+
1209
+ class LspCompletionScopeTests(unittest.TestCase):
1210
+ """Completion answers only where an item can be written."""
1211
+
1212
+ def setUp(self):
1213
+ self.server = _Server(_c_completion_catalog())
1214
+ self.addCleanup(self.server.stop)
1215
+ self.caps = self.server.start()
1216
+ self.server.open(SCOPE_C_URI, "c", SCOPE_C_TEXT)
1217
+ self.server.open(SCOPE_ASM_URI, "asm", SCOPE_ASM_TEXT)
1218
+ self.server.open(SCOPE_NASM_URI, "nasm", SCOPE_NASM_TEXT)
1219
+
1220
+ def _at(self, uri, text, line, marker, typed, occurrence=0):
1221
+ row = text.split("\n")[line]
1222
+ index = -1
1223
+ for _ in range(occurrence + 1):
1224
+ index = row.index(marker, index + 1)
1225
+ return self.server.completion(uri, line, index + typed)
1226
+
1227
+ def _labels(self, items):
1228
+ return [item["label"] for item in items]
1229
+
1230
+ def test_letters_are_not_trigger_characters(self):
1231
+ triggers = self.caps["completionProvider"]["triggerCharacters"]
1232
+ self.assertEqual(triggers, ["_", "."])
1233
+
1234
+ def test_c_line_comment_gives_no_items(self):
1235
+ self.assertEqual(self._at(SCOPE_C_URI, SCOPE_C_TEXT, 0, "vaddps", 2), [])
1236
+
1237
+ def test_c_block_comment_gives_no_items(self):
1238
+ self.assertEqual(self._at(SCOPE_C_URI, SCOPE_C_TEXT, 1, "_mm", 3), [])
1239
+
1240
+ def test_c_string_gives_no_items(self):
1241
+ self.assertEqual(self._at(SCOPE_C_URI, SCOPE_C_TEXT, 2, "_mm", 3), [])
1242
+
1243
+ def test_dot_after_identifier_in_c_gives_no_items(self):
1244
+ params = {
1245
+ "textDocument": {"uri": SCOPE_C_URI},
1246
+ "position": {"line": 3, "character": len("int y = foo.")},
1247
+ "context": {"triggerKind": 2, "triggerCharacter": "."},
1248
+ }
1249
+ result = self.server.request("textDocument/completion", params)["result"]
1250
+ self.assertEqual(result["items"], [])
1251
+
1252
+ def test_empty_prefix_gives_no_items_unless_invoked_by_hand(self):
1253
+ def ask(kind):
1254
+ params = {
1255
+ "textDocument": {"uri": SCOPE_C_URI},
1256
+ "position": {"line": 3, "character": len("int y = ")},
1257
+ "context": {"triggerKind": kind},
1258
+ }
1259
+ return self.server.request("textDocument/completion", params)["result"]["items"]
1260
+
1261
+ self.assertEqual(ask(2), [])
1262
+ self.assertTrue(ask(1))
1263
+
1264
+ def test_c_plain_code_still_completes_intrinsics(self):
1265
+ items = self._at(SCOPE_C_URI, SCOPE_C_TEXT, 4, "_mm_add", 7)
1266
+ self.assertEqual(self._labels(items), ["_mm_add_ps"])
1267
+
1268
+ def test_c_asm_template_keeps_mnemonics_at_statement_start(self):
1269
+ items = self._at(SCOPE_C_URI, SCOPE_C_TEXT, 5, "vadd", 4)
1270
+ self.assertEqual({item["kind"] for item in items}, {14})
1271
+ self.assertTrue(items)
1272
+
1273
+ def test_c_asm_template_operand_gives_no_items(self):
1274
+ self.assertEqual(self._at(SCOPE_C_URI, SCOPE_C_TEXT, 5, "vadd", 4, occurrence=1), [])
1275
+
1276
+ def test_asm_comment_lines_give_no_items(self):
1277
+ self.assertEqual(self._at(SCOPE_ASM_URI, SCOPE_ASM_TEXT, 0, "vadd", 4), [])
1278
+ self.assertEqual(self._at(SCOPE_NASM_URI, SCOPE_NASM_TEXT, 0, "vadd", 4), [])
1279
+ self.assertEqual(self._at(SCOPE_NASM_URI, SCOPE_NASM_TEXT, 1, "vadd", 4, occurrence=1), [])
1280
+
1281
+ def test_asm_operand_position_gives_no_items(self):
1282
+ self.assertEqual(self._at(SCOPE_ASM_URI, SCOPE_ASM_TEXT, 1, "vadd", 4, occurrence=1), [])
1283
+
1284
+ def test_asm_mnemonic_position_still_completes(self):
1285
+ for line, marker in ((1, "vadd"), (2, "vadd"), (3, "vadd")):
1286
+ items = self._at(SCOPE_ASM_URI, SCOPE_ASM_TEXT, line, marker, 4)
1287
+ self.assertIn("VADDPS (XMM, XMM, XMM)", self._labels(items), (line, marker))
1288
+
1289
+
1290
+ REVIEW_QUOTE_URI = "file:///tmp/simdref_lsp_review_quote.c"
1291
+ REVIEW_QUOTE_TEXT = (
1292
+ "x = '\"'; _mm_add\n"
1293
+ "__m256i q = _mm256_cmpeq_epi8(in, _mm256_set1_epi8('\"')); __m256i b = _mm256_cmpeq\n"
1294
+ "if (c=='\"') s = \"_mm\n"
1295
+ "char c = 'm\n"
1296
+ "y = '\\''; _mm_add\n"
1297
+ 'z = "a\\"_mm\n'
1298
+ )
1299
+ REVIEW_RAW_URI = "file:///tmp/simdref_lsp_review_raw.cpp"
1300
+ REVIEW_RAW_TEXT = 'auto s = R"(_mm_add\nstill _mm_add\n)" ;\nauto t = R"x(a"b)x"; _mm_add\n'
1301
+ REVIEW_MSC_URI = "file:///tmp/simdref_lsp_review_msvc.c"
1302
+ REVIEW_MSC_TEXT = (
1303
+ "void f(){ __asm { vaddps xmm0, xmm1 }\n"
1304
+ "}\n"
1305
+ "void g(){ __asm vadd\n"
1306
+ "}\n"
1307
+ "void h(){ __asm mov eax, 1 __asm vadd\n"
1308
+ "}\n"
1309
+ )
1310
+ REVIEW_OBJ_URI = "file:///tmp/simdref_lsp_review_obj.s"
1311
+ REVIEW_OBJ_TEXT = " 401000:\tc5 f4 58 c2 \tvadd ymm0, ymm1\n"
1312
+
1313
+
1314
+ class LspCompletionReviewTests(unittest.TestCase):
1315
+ """The review round repros. A char literal and a raw string are their
1316
+ own tokens. Each MSVC __asm statement starts a mnemonic position. An
1317
+ objdump row offers items at the mnemonic column only. On the fixture
1318
+ catalog, not the ambient one."""
1319
+
1320
+ def setUp(self):
1321
+ self.server = _Server(_c_completion_catalog())
1322
+ self.addCleanup(self.server.stop)
1323
+ self.server.start()
1324
+ self.server.open(REVIEW_QUOTE_URI, "c", REVIEW_QUOTE_TEXT)
1325
+ self.server.open(REVIEW_RAW_URI, "cpp", REVIEW_RAW_TEXT)
1326
+ self.server.open(REVIEW_MSC_URI, "c", REVIEW_MSC_TEXT)
1327
+ self.server.open(REVIEW_OBJ_URI, "asm", REVIEW_OBJ_TEXT)
1328
+
1329
+ def _labels(self, items):
1330
+ return [item["label"] for item in items]
1331
+
1332
+ def _end(self, uri, text, line):
1333
+ return self.server.completion(uri, line, len(text.split("\n")[line]))
1334
+
1335
+ def _at(self, uri, text, line, marker, typed, occurrence=0):
1336
+ row = text.split("\n")[line]
1337
+ index = -1
1338
+ for _ in range(occurrence + 1):
1339
+ index = row.index(marker, index + 1)
1340
+ return self.server.completion(uri, line, index + typed)
1341
+
1342
+ def test_char_literal_quote_keeps_the_rest_of_the_row_code(self):
1343
+ items = self._end(REVIEW_QUOTE_URI, REVIEW_QUOTE_TEXT, 0)
1344
+ self.assertEqual(self._labels(items), ["_mm_add_ps"])
1345
+
1346
+ def test_char_literal_quote_before_an_intrinsic_name(self):
1347
+ items = self._end(REVIEW_QUOTE_URI, REVIEW_QUOTE_TEXT, 1)
1348
+ self.assertEqual(self._labels(items), ["_mm256_cmpeq_epi8"])
1349
+
1350
+ def test_char_literal_quote_before_a_string_keeps_the_string(self):
1351
+ self.assertEqual(self._end(REVIEW_QUOTE_URI, REVIEW_QUOTE_TEXT, 2), [])
1352
+
1353
+ def test_char_literal_tokens_stay_closed(self):
1354
+ self.assertEqual(self._end(REVIEW_QUOTE_URI, REVIEW_QUOTE_TEXT, 3), [])
1355
+ items = self._end(REVIEW_QUOTE_URI, REVIEW_QUOTE_TEXT, 4)
1356
+ self.assertEqual(self._labels(items), ["_mm_add_ps"])
1357
+ self.assertEqual(self._end(REVIEW_QUOTE_URI, REVIEW_QUOTE_TEXT, 5), [])
1358
+
1359
+ def test_raw_string_keeps_its_body_on_the_next_rows(self):
1360
+ self.assertEqual(self._end(REVIEW_RAW_URI, REVIEW_RAW_TEXT, 0), [])
1361
+ self.assertEqual(self._end(REVIEW_RAW_URI, REVIEW_RAW_TEXT, 1), [])
1362
+ uri = "file:///tmp/simdref_lsp_review_raw_eof.cpp"
1363
+ text = 'auto s = R"(_mm_add'
1364
+ self.server.open(uri, "cpp", text)
1365
+ self.assertEqual(self.server.completion(uri, 0, len(text)), [])
1366
+
1367
+ def test_raw_string_delimited_body_keeps_its_quotes(self):
1368
+ items = self._end(REVIEW_RAW_URI, REVIEW_RAW_TEXT, 3)
1369
+ self.assertEqual(self._labels(items), ["_mm_add_ps"])
1370
+
1371
+ def test_msvc_brace_statement_completes_at_the_mnemonic(self):
1372
+ items = self._at(REVIEW_MSC_URI, REVIEW_MSC_TEXT, 0, "vaddps", 6)
1373
+ self.assertEqual(self._labels(items), ["VADDPS (XMM, XMM, XMM)"])
1374
+ self.assertEqual(self._at(REVIEW_MSC_URI, REVIEW_MSC_TEXT, 0, "xmm1", 2), [])
1375
+
1376
+ def test_msvc_row_statement_after_code_completes(self):
1377
+ items = self._end(REVIEW_MSC_URI, REVIEW_MSC_TEXT, 2)
1378
+ self.assertEqual(self._labels(items), ["VADDPS (XMM, XMM, XMM)"])
1379
+
1380
+ def test_msvc_second_statement_on_one_row_completes(self):
1381
+ items = self._end(REVIEW_MSC_URI, REVIEW_MSC_TEXT, 4)
1382
+ self.assertEqual(self._labels(items), ["VADDPS (XMM, XMM, XMM)"])
1383
+ items = self._at(REVIEW_MSC_URI, REVIEW_MSC_TEXT, 4, "mov", 3)
1384
+ self.assertEqual(self._labels(items), ["MOV (R64, R64)"])
1385
+ self.assertEqual(self._at(REVIEW_MSC_URI, REVIEW_MSC_TEXT, 4, "eax", 2), [])
1386
+
1387
+ def test_objdump_row_completes_at_the_mnemonic_column(self):
1388
+ items = self._at(REVIEW_OBJ_URI, REVIEW_OBJ_TEXT, 0, "vadd", 4)
1389
+ self.assertEqual(sorted(self._labels(items)), ["VADDPS (XMM, XMM, XMM)", "vaddq_f32"])
1390
+
1391
+ def test_objdump_byte_column_gives_no_items(self):
1392
+ self.assertEqual(self._at(REVIEW_OBJ_URI, REVIEW_OBJ_TEXT, 0, "c5", 1), [])
1393
+ self.assertEqual(self._at(REVIEW_OBJ_URI, REVIEW_OBJ_TEXT, 0, "ymm0", 2), [])
1394
+
1395
+
1187
1396
  class MissingCatalogTests(unittest.TestCase):
1188
1397
  def test_missing_catalog_sends_show_message_once(self):
1189
1398
  server = _Server(MISSING_CATALOG)
File without changes
File without changes
File without changes