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.
- {simdref-0.0.18/src/simdref.egg-info → simdref-0.0.19}/PKG-INFO +1 -1
- {simdref-0.0.18 → simdref-0.0.19}/pyproject.toml +1 -1
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/asmline.py +132 -12
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/lsp.py +125 -12
- {simdref-0.0.18 → simdref-0.0.19/src/simdref.egg-info}/PKG-INFO +1 -1
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_lsp_ptx.py +19 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_lsp_stdio.py +212 -3
- {simdref-0.0.18 → simdref-0.0.19}/LICENSE +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/README.md +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/setup.cfg +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/__init__.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/__main__.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/annotate.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/arm_instructions.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/asm_identity.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/catkey.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/cli.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/cuda.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/display.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/export.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/filters.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/htmltext.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/ingest.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/ingest_catalog.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/ingest_pdf.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/ingest_sources.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/manpages.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/models.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/operands.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/pdfparse/__init__.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/pdfparse/base.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/pdfparse/intel.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/pdfparse/registry.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/pdfparse/types.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/pdfrefs.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf_sources/__init__.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf_sources/amd_swopt.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf_sources/arm_swopt.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf_sources/cores.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf_sources/instlatx64.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf_sources/intel_guide.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf_sources/intel_optref.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf_sources/llvm_mca.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf_sources/llvm_scheduling.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/perf_sources/merge.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/ptx.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/queries.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/regtable.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/riscv.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/search.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/storage.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/tablecols.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/tui.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref/ui_labels.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref.egg-info/SOURCES.txt +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref.egg-info/dependency_links.txt +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref.egg-info/entry_points.txt +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref.egg-info/requires.txt +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/src/simdref.egg-info/top_level.txt +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_a1_sdm_parser.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_a2_perf_rule.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_a4_arm_pdfrefs.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_a5_offline_aarchmrs.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_amd_match_llvm_mc.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_annotate.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_annotate_positions.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_annotate_scans.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_arrangement_tail.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_asm_identity.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_asm_wire.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_asmline.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_audit_coverage.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_auto_update_on_version_change.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_b1_latency_pairs.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_b2_metadata.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_b3_sdm_forms.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_b4_descriptions_provenance.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_b5_linking.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_b5_perf_family.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_b5_sme.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_c0_perf_priority.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_catkey.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_cli_bare_query.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_cli_bootstrap_progress.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_cli_help.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_cli_llm.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_coverage_live.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_coverage_parity.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_cuda_declaration_parser.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_cuda_intrinsics.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_cuda_td_expansion.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_cuda_wiring.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_display.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_display_scanners.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_export.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_export_viz.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_fetch_retry.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_filters.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_fixui_display.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_htmltext.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_ingest_enc_fields.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_ingest_sources.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_int_arm_isa_label.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_int_core_ids.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_int_ingest_registration.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_int_sdm_family_fallback.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_intel_operation_and_url.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_isa_detect_llvm_mc.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue2_fixes.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_51.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_52.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_53_54.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_55.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_56_57a.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_57_cpi.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_60_c1.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_60_contract.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_60_crossarch.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_60_matcher.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_arm_alias.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_branch_target.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_compressed.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_foreign_arch.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_identity.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_m1.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_m5.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_perfdata.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_sme_za.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_text.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issue_65_timing.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issues_23_24_25.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_issues_51_57_join.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_leftover_arm_form_pick.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_leftover_arm_postindex.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_leftover_att_movx.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_leftover_autoupdate.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_leftover_catalog_gaps.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_leftover_ingest_isa_merge.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_leftover_intel_size.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_leftover_perf_join.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_leftover_riscv_shared.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_lsp_cuda.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_lsp_web.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_models.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_no_catalog_row.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_objdump_listings.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_operands.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_pdfparse.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_perf.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_perf_source_amd_swopt.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_perf_source_arm_swopt.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_perf_source_instlatx64.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_perf_source_intel_guide.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_perf_source_intel_optref.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_perf_sources.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_presentation.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_preset_persistence.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_ptx.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_regtable.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_release_tag_pin.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_riscv_docs_pages.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_search.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_search_pushdown.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_shared_source_helpers.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_source_kind_filter.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_source_validation.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_storage_alnum.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_storage_payload.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_storage_schema.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_tablecols.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_tui.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_ui_labels_parity.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_update_build_stamp.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_vendor_riscv_categories.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_vendor_riscv_sources.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_version_matches_pyproject.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_x86_key_widths.py +0 -0
- {simdref-0.0.18 → simdref-0.0.19}/tests/test_x86_linking.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "simdref"
|
|
7
|
-
version = "0.0.
|
|
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
|
|
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
|
|
187
|
-
if
|
|
188
|
-
return
|
|
189
|
-
for start,
|
|
190
|
-
if start <= offset <= start + len(
|
|
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
|
|
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
|
|
244
|
-
|
|
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).
|
|
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 =
|
|
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,
|
|
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
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
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
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
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": ["_", "."
|
|
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(
|
|
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
|
)
|
|
@@ -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
|
|
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(
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|