diff --git a/en/clice/cli/query.md b/en/clice/cli/query.md index 6a5d4c853..756cdf8a3 100644 --- a/en/clice/cli/query.md +++ b/en/clice/cli/query.md @@ -13,6 +13,7 @@ - `documentSymbols --path ` is the file's outline. - `compileCommand --path ` is the command the editor would compile the file with, and where it came from: the file's own database entry, a host source for a header, a rule's default command, a command borrowed from a nearby unit — for a provisional member (`provisional`) or a file outside the build (`inferred`) — or the fallback. The command is the one the compiler resolves it to, with its target and system include directories; when asking the compiler failed, `toolchainError` says why and the command stays the unresolved driver command. - `projectFiles [--filter all|source|header|module]` lists the build's files; `fileDeps --path [--direction includes|includers|both] [--depth ]` and `impactAnalysis --path ` follow the include graph. +- `counterparts --path ` lists the files the file pairs with — a header's sources, a source's headers, a module's interface and implementation units — best first, each with its reasons, and names the one an editor would open without asking as `preferred` (see [Switch Source/Header](../features/navigation.md#switch-source-header)). Paths are workspace-relative or absolute in questions, absolute in answers; lines are 1-based. diff --git a/en/clice/features/navigation.md b/en/clice/features/navigation.md index 06e0cf394..cafd7e9cc 100644 --- a/en/clice/features/navigation.md +++ b/en/clice/features/navigation.md @@ -1693,17 +1693,112 @@ tests/snap/document_highlight/document_highlight/11_highlight_module_unit/main.c ## Switch Source/Header +Open the file the current one pairs with: a header's source, a source's header, a module interface's implementation units, an implementation unit's interface. VS Code runs it as **Clice: Switch Source/Header** (`Alt+O`), Neovim as `:LspCliceSwitchSourceHeader`, and `clice query --method counterparts --path ` answers the same on the command line. Zed lets no extension add a command, and its own _switch source header_ works with clangd only. + +The candidates come from what the build and the index already know, so nothing is compiled for the answer: the files of the same name on the other side — in the file's own directory, or elsewhere in the workspace when one includes the other —, the declarations one holds that the other defines, and module declarations. Each candidate carries its reasons. The editor opens the best one directly when it is the only candidate or beats every other: it has each of the other's reasons among shared declarations and the same name, at least as many shared declarations, and either a reason more, twice the shared declarations when both share some, or — neither sharing any — a module pairing the other lacks. Otherwise it lists the candidates to pick from. + - + + +**Header and its source** + +A header and the source of the same name that defines what it declares switch to each other directly + +```snap +tests/snap/switch_source_header/switch_source_header/01_header_source/main.cpp +``` + + + + + +**Headers apart from sources** + +A header under `include/` finds its source under `src/`, ahead of another file of the same name that defines none of its declarations + +Away from the header's own directory, a file of the same name counts +only when one of the two includes the other — `tools/shape.cpp` includes +nothing and is no counterpart — and of those only the nearest: +`src/legacy/shape.cpp` lies a directory deeper than `src/shape.cpp`. + +```snap +tests/snap/switch_source_header/switch_source_header/02_split_layout/main.cpp +``` + + + + + +**Definitions across several sources** + +When several sources define a header's declarations, the one defining clearly the most is taken directly and the others stay listed + +```snap +tests/snap/switch_source_header/switch_source_header/03_scattered_definitions/main.cpp +``` + + + + + +**No clear counterpart** + +When no candidate clearly outweighs the others, the editor lists them with their reasons to pick from + +```snap +tests/snap/switch_source_header/switch_source_header/04_split_evenly/main.cpp +``` + + + + + +**Module interface and implementations** + +A module interface unit pairs with the units implementing its module, and each of them with the interface + +```snap +tests/snap/switch_source_header/switch_source_header/05_module_units/main.cpp +``` + + + + + +**Partition beside its implementation** + +An internal partition and the implementation unit of the same name defining its declarations switch to each other directly + +The implementation unit also lists the primary interface of its module, +after the partition. + +```snap +tests/snap/switch_source_header/switch_source_header/06_module_partition/main.cpp +``` + + + + + +**Files without a counterpart** + +A header that defines everything it declares, and a `.def` fragment, have no counterpart + +```snap +tests/snap/switch_source_header/switch_source_header/07_no_counterpart/main.cpp +``` + + + + -**Source-header switching** +**Template definitions apart** -Source/header switching is not implemented, so users cannot jump directly -between paired files +A header and the `.tpp` file it includes for its template definitions switch to each other ```snap -tests/snap/navigation/switch_source_header/01_switch_source_header.cpp +tests/snap/switch_source_header/switch_source_header/08_template_definitions/main.cpp ``` diff --git a/en/clice/features/overview.md b/en/clice/features/overview.md index 0bef348ca..90553f16a 100644 --- a/en/clice/features/overview.md +++ b/en/clice/features/overview.md @@ -18,7 +18,7 @@ Language Server Protocol features available when using clice as an editor backen | [Code Completion](./completion.md) | 57 supported | | [Hover](./hover.md) | 38 supported · 19 partial · 10 unsupported | | [Signature Help](./signature-help.md) | 14 supported | -| [Code Navigation](./navigation.md) | 71 supported · 12 partial · 26 unsupported | +| [Code Navigation](./navigation.md) | 79 supported · 12 partial · 25 unsupported | | [Document Links](./document-links.md) | 7 supported · 1 partial · 1 unsupported | | [Semantic Tokens](./semantic-tokens.md) | 56 supported · 2 partial · 10 unsupported | | [Inlay Hints](./inlay-hints.md) | 34 supported · 6 partial · 4 unsupported | diff --git a/sources/clice/tests/snap/navigation/switch_source_header/01_switch_source_header.cpp b/sources/clice/tests/snap/navigation/switch_source_header/01_switch_source_header.cpp deleted file mode 100644 index 399b331b9..000000000 --- a/sources/clice/tests/snap/navigation/switch_source_header/01_switch_source_header.cpp +++ /dev/null @@ -1,14 +0,0 @@ -/// # Source-header switching -/// -/// - status: unsupported -/// -/// Source/header switching is not implemented, so users cannot jump directly -/// between paired files - -// widget.h -class Widget { - void draw(); -}; - -// widget.cpp — #include "widget.h" -void Widget::draw() {} diff --git a/sources/clice/tests/snap/switch_source_header/.clang-format b/sources/clice/tests/snap/switch_source_header/.clang-format new file mode 100644 index 000000000..1d59852f3 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/.clang-format @@ -0,0 +1,4 @@ +# The fixtures' doc headers and code feed the docs as written, so +# formatting is disabled for this directory. +DisableFormat: true +SortIncludes: false diff --git a/sources/clice/tests/snap/switch_source_header/corpus.json b/sources/clice/tests/snap/switch_source_header/corpus.json new file mode 100644 index 000000000..a041a3629 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/corpus.json @@ -0,0 +1,4 @@ +{ + "notes": "Freestanding, -undef and -nostdinc keep host headers out of the build, so the files a reply names are the fixture's own on every host. Fixtures declare the files to ask about as `// switch:` comment lines in the entry source.", + "flags": ["-std=c++23", "-ffreestanding", "-undef", "-nostdinc"] +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/01_header_source/main.cpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/01_header_source/main.cpp new file mode 100644 index 000000000..db295267f --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/01_header_source/main.cpp @@ -0,0 +1,17 @@ +/// # Header and its source +/// +/// - status: supported +/// - verify: server +/// - indexing: true +/// +/// A header and the source of the same name that defines what it declares switch to each other directly + +// switch: widget.h +// switch: widget.cpp + +#include "widget.h" + +int render(Widget& widget) { + widget.draw(); + return widget.area(); +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/01_header_source/main.snap.yml b/sources/clice/tests/snap/switch_source_header/switch_source_header/01_header_source/main.snap.yml new file mode 100644 index 000000000..09fcbe022 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/01_header_source/main.snap.yml @@ -0,0 +1,13 @@ +--- +created_at: 2026-10-09 +input_file: switch_source_header/01_header_source/main.cpp +--- +"widget.h": + preferred: "${WS}/switch_source_header/01_header_source/widget.cpp" + candidates: + - { file: "${WS}/switch_source_header/01_header_source/widget.cpp", reasons: ["defines 2 of 2 declarations", "same name"] } + +"widget.cpp": + preferred: "${WS}/switch_source_header/01_header_source/widget.h" + candidates: + - { file: "${WS}/switch_source_header/01_header_source/widget.h", reasons: ["declares 2 of 2 definitions", "same name"] } diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/01_header_source/widget.cpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/01_header_source/widget.cpp new file mode 100644 index 000000000..2f166def1 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/01_header_source/widget.cpp @@ -0,0 +1,7 @@ +#include "widget.h" + +void Widget::draw() {} + +int Widget::area() const { + return width * height; +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/01_header_source/widget.h b/sources/clice/tests/snap/switch_source_header/switch_source_header/01_header_source/widget.h new file mode 100644 index 000000000..885570485 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/01_header_source/widget.h @@ -0,0 +1,11 @@ +#pragma once + +class Widget { +public: + void draw(); + int area() const; + +private: + int width = 0; + int height = 0; +}; diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/include/geometry/shape.h b/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/include/geometry/shape.h new file mode 100644 index 000000000..fdd369471 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/include/geometry/shape.h @@ -0,0 +1,4 @@ +#pragma once + +int shape_area(int width, int height); +int shape_perimeter(int width, int height); diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/main.cpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/main.cpp new file mode 100644 index 000000000..2e7b32fda --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/main.cpp @@ -0,0 +1,21 @@ +/// # Headers apart from sources +/// +/// - status: supported +/// - verify: server +/// - indexing: true +/// +/// A header under `include/` finds its source under `src/`, ahead of another file of the same name that defines none of its declarations +/// +/// Away from the header's own directory, a file of the same name counts +/// only when one of the two includes the other — `tools/shape.cpp` includes +/// nothing and is no counterpart — and of those only the nearest: +/// `src/legacy/shape.cpp` lies a directory deeper than `src/shape.cpp`. + +// switch: include/geometry/shape.h +// switch: src/shape.cpp + +#include "include/geometry/shape.h" + +int total(int width, int height) { + return shape_area(width, height) + shape_perimeter(width, height); +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/main.snap.yml b/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/main.snap.yml new file mode 100644 index 000000000..a8dc086de --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/main.snap.yml @@ -0,0 +1,14 @@ +--- +created_at: 2026-10-09 +input_file: switch_source_header/02_split_layout/main.cpp +--- +"include/geometry/shape.h": + preferred: "${WS}/switch_source_header/02_split_layout/src/shape.cpp" + candidates: + - { file: "${WS}/switch_source_header/02_split_layout/src/shape.cpp", reasons: ["defines 2 of 2 declarations", "same name"] } + - { file: "${WS}/switch_source_header/02_split_layout/tests/shape.cpp", reasons: ["same name"] } + +"src/shape.cpp": + preferred: "${WS}/switch_source_header/02_split_layout/include/geometry/shape.h" + candidates: + - { file: "${WS}/switch_source_header/02_split_layout/include/geometry/shape.h", reasons: ["declares 2 of 2 definitions", "same name"] } diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/src/legacy/shape.cpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/src/legacy/shape.cpp new file mode 100644 index 000000000..c8580c58a --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/src/legacy/shape.cpp @@ -0,0 +1,5 @@ +#include "../../include/geometry/shape.h" + +int legacy_area(int width, int height) { + return shape_area(height, width); +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/src/shape.cpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/src/shape.cpp new file mode 100644 index 000000000..09accba35 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/src/shape.cpp @@ -0,0 +1,9 @@ +#include "../include/geometry/shape.h" + +int shape_area(int width, int height) { + return width * height; +} + +int shape_perimeter(int width, int height) { + return 2 * (width + height); +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/tests/shape.cpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/tests/shape.cpp new file mode 100644 index 000000000..af0d7966b --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/tests/shape.cpp @@ -0,0 +1,5 @@ +#include "../include/geometry/shape.h" + +int check_shape() { + return shape_area(2, 3) == 6 ? 0 : 1; +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/tools/shape.cpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/tools/shape.cpp new file mode 100644 index 000000000..3c08116e1 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/02_split_layout/tools/shape.cpp @@ -0,0 +1,3 @@ +int draw_shape(int sides) { + return sides * 2; +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/03_scattered_definitions/main.cpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/03_scattered_definitions/main.cpp new file mode 100644 index 000000000..6a54b6ae0 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/03_scattered_definitions/main.cpp @@ -0,0 +1,16 @@ +/// # Definitions across several sources +/// +/// - status: supported +/// - verify: server +/// - indexing: true +/// +/// When several sources define a header's declarations, the one defining clearly the most is taken directly and the others stay listed + +// switch: text.h +// switch: text_case.cpp + +#include "text.h" + +int process(char* text) { + return trim(text) + split(text) + join(text) + upper(text); +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/03_scattered_definitions/main.snap.yml b/sources/clice/tests/snap/switch_source_header/switch_source_header/03_scattered_definitions/main.snap.yml new file mode 100644 index 000000000..70bd0353e --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/03_scattered_definitions/main.snap.yml @@ -0,0 +1,14 @@ +--- +created_at: 2026-10-09 +input_file: switch_source_header/03_scattered_definitions/main.cpp +--- +"text.h": + preferred: "${WS}/switch_source_header/03_scattered_definitions/text_ops.cpp" + candidates: + - { file: "${WS}/switch_source_header/03_scattered_definitions/text_ops.cpp", reasons: ["defines 3 of 4 declarations"] } + - { file: "${WS}/switch_source_header/03_scattered_definitions/text_case.cpp", reasons: ["defines 1 of 4 declarations"] } + +"text_case.cpp": + preferred: "${WS}/switch_source_header/03_scattered_definitions/text.h" + candidates: + - { file: "${WS}/switch_source_header/03_scattered_definitions/text.h", reasons: ["declares 1 of 1 definition"] } diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/03_scattered_definitions/text.h b/sources/clice/tests/snap/switch_source_header/switch_source_header/03_scattered_definitions/text.h new file mode 100644 index 000000000..1de154c90 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/03_scattered_definitions/text.h @@ -0,0 +1,6 @@ +#pragma once + +int trim(char* text); +int split(char* text); +int join(char* text); +int upper(char* text); diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/03_scattered_definitions/text_case.cpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/03_scattered_definitions/text_case.cpp new file mode 100644 index 000000000..1ebb85053 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/03_scattered_definitions/text_case.cpp @@ -0,0 +1,5 @@ +#include "text.h" + +int upper(char* text) { + return text[0] - 32; +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/03_scattered_definitions/text_ops.cpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/03_scattered_definitions/text_ops.cpp new file mode 100644 index 000000000..ef3f9b4ab --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/03_scattered_definitions/text_ops.cpp @@ -0,0 +1,13 @@ +#include "text.h" + +int trim(char* text) { + return text[0]; +} + +int split(char* text) { + return text[1]; +} + +int join(char* text) { + return text[2]; +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/04_split_evenly/codec.h b/sources/clice/tests/snap/switch_source_header/switch_source_header/04_split_evenly/codec.h new file mode 100644 index 000000000..2e0871c32 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/04_split_evenly/codec.h @@ -0,0 +1,4 @@ +#pragma once + +int encode(int value); +int decode(int value); diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/04_split_evenly/decode.cpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/04_split_evenly/decode.cpp new file mode 100644 index 000000000..f99dd61a9 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/04_split_evenly/decode.cpp @@ -0,0 +1,5 @@ +#include "codec.h" + +int decode(int value) { + return value ^ 0x5a; +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/04_split_evenly/encode.cpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/04_split_evenly/encode.cpp new file mode 100644 index 000000000..f1e947154 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/04_split_evenly/encode.cpp @@ -0,0 +1,5 @@ +#include "codec.h" + +int encode(int value) { + return value ^ 0x5a; +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/04_split_evenly/main.cpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/04_split_evenly/main.cpp new file mode 100644 index 000000000..a2563e35c --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/04_split_evenly/main.cpp @@ -0,0 +1,15 @@ +/// # No clear counterpart +/// +/// - status: supported +/// - verify: server +/// - indexing: true +/// +/// When no candidate clearly outweighs the others, the editor lists them with their reasons to pick from + +// switch: codec.h + +#include "codec.h" + +int round_trip(int value) { + return decode(encode(value)); +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/04_split_evenly/main.snap.yml b/sources/clice/tests/snap/switch_source_header/switch_source_header/04_split_evenly/main.snap.yml new file mode 100644 index 000000000..2d3ba44b3 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/04_split_evenly/main.snap.yml @@ -0,0 +1,8 @@ +--- +created_at: 2026-10-09 +input_file: switch_source_header/04_split_evenly/main.cpp +--- +"codec.h": + candidates: + - { file: "${WS}/switch_source_header/04_split_evenly/decode.cpp", reasons: ["defines 1 of 2 declarations"] } + - { file: "${WS}/switch_source_header/04_split_evenly/encode.cpp", reasons: ["defines 1 of 2 declarations"] } diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/05_module_units/main.cpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/05_module_units/main.cpp new file mode 100644 index 000000000..1e6680bc8 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/05_module_units/main.cpp @@ -0,0 +1,16 @@ +/// # Module interface and implementations +/// +/// - status: supported +/// - verify: server +/// - indexing: true +/// +/// A module interface unit pairs with the units implementing its module, and each of them with the interface + +// switch: shapes.cppm +// switch: shapes_debug.cpp + +import shapes; + +int total(int width, int height) { + return area(width, height) + perimeter(width, height); +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/05_module_units/main.snap.yml b/sources/clice/tests/snap/switch_source_header/switch_source_header/05_module_units/main.snap.yml new file mode 100644 index 000000000..253240b50 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/05_module_units/main.snap.yml @@ -0,0 +1,14 @@ +--- +created_at: 2026-10-09 +input_file: switch_source_header/05_module_units/main.cpp +--- +"shapes.cppm": + preferred: "${WS}/switch_source_header/05_module_units/shapes.cpp" + candidates: + - { file: "${WS}/switch_source_header/05_module_units/shapes.cpp", reasons: ["defines 2 of 2 declarations", "same name", "implements module shapes"] } + - { file: "${WS}/switch_source_header/05_module_units/shapes_debug.cpp", reasons: ["implements module shapes"] } + +"shapes_debug.cpp": + preferred: "${WS}/switch_source_header/05_module_units/shapes.cppm" + candidates: + - { file: "${WS}/switch_source_header/05_module_units/shapes.cppm", reasons: ["interface of module shapes"] } diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/05_module_units/shapes.cpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/05_module_units/shapes.cpp new file mode 100644 index 000000000..a058b4f3d --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/05_module_units/shapes.cpp @@ -0,0 +1,9 @@ +module shapes; + +int area(int width, int height) { + return width * height; +} + +int perimeter(int width, int height) { + return 2 * (width + height); +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/05_module_units/shapes.cppm b/sources/clice/tests/snap/switch_source_header/switch_source_header/05_module_units/shapes.cppm new file mode 100644 index 000000000..87acc45ce --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/05_module_units/shapes.cppm @@ -0,0 +1,4 @@ +export module shapes; + +export int area(int width, int height); +export int perimeter(int width, int height); diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/05_module_units/shapes_debug.cpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/05_module_units/shapes_debug.cpp new file mode 100644 index 000000000..67244b318 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/05_module_units/shapes_debug.cpp @@ -0,0 +1,5 @@ +module shapes; + +int debug_area() { + return area(1, 1); +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/06_module_partition/api.cpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/06_module_partition/api.cpp new file mode 100644 index 000000000..2067680e0 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/06_module_partition/api.cpp @@ -0,0 +1,7 @@ +module lib; + +import :api; + +int lookup(int key) { + return key * 2; +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/06_module_partition/api.cppm b/sources/clice/tests/snap/switch_source_header/switch_source_header/06_module_partition/api.cppm new file mode 100644 index 000000000..661cb2940 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/06_module_partition/api.cppm @@ -0,0 +1,3 @@ +module lib:api; + +int lookup(int key); diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/06_module_partition/lib.cppm b/sources/clice/tests/snap/switch_source_header/switch_source_header/06_module_partition/lib.cppm new file mode 100644 index 000000000..066c2e195 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/06_module_partition/lib.cppm @@ -0,0 +1 @@ +export module lib; diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/06_module_partition/main.cpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/06_module_partition/main.cpp new file mode 100644 index 000000000..de7995885 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/06_module_partition/main.cpp @@ -0,0 +1,21 @@ +/// # Partition beside its implementation +/// +/// - status: supported +/// - verify: server +/// - indexing: true +/// +/// An internal partition and the implementation unit of the same name defining its declarations switch to each other directly +/// +/// The implementation unit also lists the primary interface of its module, +/// after the partition. + +// switch: api.cppm +// switch: api.cpp + +module lib; + +import :api; + +int run() { + return lookup(1); +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/06_module_partition/main.snap.yml b/sources/clice/tests/snap/switch_source_header/switch_source_header/06_module_partition/main.snap.yml new file mode 100644 index 000000000..d96316a4e --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/06_module_partition/main.snap.yml @@ -0,0 +1,14 @@ +--- +created_at: 2026-10-09 +input_file: switch_source_header/06_module_partition/main.cpp +--- +"api.cppm": + preferred: "${WS}/switch_source_header/06_module_partition/api.cpp" + candidates: + - { file: "${WS}/switch_source_header/06_module_partition/api.cpp", reasons: ["defines 1 of 1 declaration", "same name"] } + +"api.cpp": + preferred: "${WS}/switch_source_header/06_module_partition/api.cppm" + candidates: + - { file: "${WS}/switch_source_header/06_module_partition/api.cppm", reasons: ["declares 1 of 1 definition", "same name"] } + - { file: "${WS}/switch_source_header/06_module_partition/lib.cppm", reasons: ["interface of module lib"] } diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/07_no_counterpart/colors.def b/sources/clice/tests/snap/switch_source_header/switch_source_header/07_no_counterpart/colors.def new file mode 100644 index 000000000..d4d270b13 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/07_no_counterpart/colors.def @@ -0,0 +1,3 @@ +COLOR(red) +COLOR(green) +COLOR(blue) diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/07_no_counterpart/main.cpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/07_no_counterpart/main.cpp new file mode 100644 index 000000000..349a93572 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/07_no_counterpart/main.cpp @@ -0,0 +1,22 @@ +/// # Files without a counterpart +/// +/// - status: supported +/// - verify: server +/// - indexing: true +/// +/// A header that defines everything it declares, and a `.def` fragment, have no counterpart + +// switch: math.h +// switch: colors.def + +#include "math.h" + +enum class Color { +#define COLOR(name) name, +#include "colors.def" +#undef COLOR +}; + +int twice_square(int value) { + return twice(square(value)); +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/07_no_counterpart/main.snap.yml b/sources/clice/tests/snap/switch_source_header/switch_source_header/07_no_counterpart/main.snap.yml new file mode 100644 index 000000000..1c433b673 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/07_no_counterpart/main.snap.yml @@ -0,0 +1,7 @@ +--- +created_at: 2026-10-09 +input_file: switch_source_header/07_no_counterpart/main.cpp +--- +"math.h": none + +"colors.def": none diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/07_no_counterpart/math.h b/sources/clice/tests/snap/switch_source_header/switch_source_header/07_no_counterpart/math.h new file mode 100644 index 000000000..8e23fddde --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/07_no_counterpart/math.h @@ -0,0 +1,9 @@ +#pragma once + +inline int square(int value) { + return value * value; +} + +inline int twice(int value) { + return 2 * value; +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/08_template_definitions/main.cpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/08_template_definitions/main.cpp new file mode 100644 index 000000000..a828dcce5 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/08_template_definitions/main.cpp @@ -0,0 +1,16 @@ +/// # Template definitions apart +/// +/// - status: supported +/// - verify: server +/// - indexing: true +/// +/// A header and the `.tpp` file it includes for its template definitions switch to each other + +// switch: matrix.h +// switch: matrix.tpp + +#include "matrix.h" + +int trace(Matrix& matrix) { + return matrix.at(0, 0) + matrix.at(1, 1); +} diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/08_template_definitions/main.snap.yml b/sources/clice/tests/snap/switch_source_header/switch_source_header/08_template_definitions/main.snap.yml new file mode 100644 index 000000000..aaf172997 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/08_template_definitions/main.snap.yml @@ -0,0 +1,13 @@ +--- +created_at: 2026-10-09 +input_file: switch_source_header/08_template_definitions/main.cpp +--- +"matrix.h": + preferred: "${WS}/switch_source_header/08_template_definitions/matrix.tpp" + candidates: + - { file: "${WS}/switch_source_header/08_template_definitions/matrix.tpp", reasons: ["defines 2 of 2 declarations", "same name"] } + +"matrix.tpp": + preferred: "${WS}/switch_source_header/08_template_definitions/matrix.h" + candidates: + - { file: "${WS}/switch_source_header/08_template_definitions/matrix.h", reasons: ["declares 2 of 2 definitions", "same name"] } diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/08_template_definitions/matrix.h b/sources/clice/tests/snap/switch_source_header/switch_source_header/08_template_definitions/matrix.h new file mode 100644 index 000000000..c29a2f1f5 --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/08_template_definitions/matrix.h @@ -0,0 +1,13 @@ +#pragma once + +template +class Matrix { +public: + T at(int row, int column) const; + void set(int row, int column, T value); + +private: + T cells[4] = {}; +}; + +#include "matrix.tpp" diff --git a/sources/clice/tests/snap/switch_source_header/switch_source_header/08_template_definitions/matrix.tpp b/sources/clice/tests/snap/switch_source_header/switch_source_header/08_template_definitions/matrix.tpp new file mode 100644 index 000000000..c5b7ad03f --- /dev/null +++ b/sources/clice/tests/snap/switch_source_header/switch_source_header/08_template_definitions/matrix.tpp @@ -0,0 +1,9 @@ +template +T Matrix::at(int row, int column) const { + return cells[row * 2 + column]; +} + +template +void Matrix::set(int row, int column, T value) { + cells[row * 2 + column] = value; +} diff --git a/zh/clice/cli/query.md b/zh/clice/cli/query.md index e435bad7f..7fc42842a 100644 --- a/zh/clice/cli/query.md +++ b/zh/clice/cli/query.md @@ -13,6 +13,7 @@ - `documentSymbols --path ` 给出该文件的大纲。 - `compileCommand --path ` 给出编辑器编译该文件时会使用的命令,以及它的来源:文件自身的数据库条目、头文件的宿主源文件、规则的默认命令、从邻近翻译单元借来的命令(借给临时成员时为 `provisional`,借给构建之外的文件时为 `inferred`),或内置的回退命令。给出的是经编译器解析后的命令,带有目标平台和系统头文件目录;询问编译器失败时,`toolchainError` 说明原因,命令则停留在未经解析的驱动命令。 - `projectFiles [--filter all|source|header|module]` 列出构建涉及的文件;`fileDeps --path [--direction includes|includers|both] [--depth ]` 和 `impactAnalysis --path ` 则沿包含关系图查询。 +- `counterparts --path ` 列出与该文件配对的文件——头文件的源文件、源文件的头文件、模块的接口单元和实现单元——最佳的排在最前,每个都附上理由,并把编辑器无需询问就会直接打开的那一个标为 `preferred`(参见[切换源文件/头文件](../features/navigation.md#switch-source-header))。 问题中的路径可以相对于工作区,也可以是绝对路径,答案中的路径一律是绝对路径;行号从 1 开始。 diff --git a/zh/clice/features/navigation.md b/zh/clice/features/navigation.md index 76eac3df5..ecf990795 100644 --- a/zh/clice/features/navigation.md +++ b/zh/clice/features/navigation.md @@ -1504,16 +1504,108 @@ tests/snap/document_highlight/document_highlight/11_highlight_module_unit/main.c ## 切换源文件/头文件 +打开与当前文件配对的文件:头文件的源文件、源文件的头文件、模块接口的实现单元、实现单元的接口。VS Code 中对应的命令是 **Clice: Switch Source/Header**(`Alt+O`),Neovim 中是 `:LspCliceSwitchSourceHeader`,命令行上则由 `clice query --method counterparts --path ` 给出同样的答案。Zed 不允许扩展添加命令,它自带的 _switch source header_ 只适用于 clangd。 + +候选文件来自构建和索引已经掌握的信息,因此给出答案无需编译任何东西:另一侧的同名文件(位于该文件自己的目录中,或者位于工作区的其他位置、但两者之间有包含关系)、一方声明而另一方定义的那些声明,以及模块声明。每个候选都附有理由。最佳候选是唯一的候选,或者胜过其余每个候选时,编辑器会直接打开它。胜过对方需要同时满足:在共享声明和同名这两项理由中,对方具备的它都具备;共享声明不比对方少;并且理由比对方多一项,或者在双方都有共享声明时达到对方的两倍,或者在双方都没有共享声明时具备对方没有的模块配对。否则编辑器会列出这些候选,供用户挑选。 + - + + +**头文件与其源文件** + +头文件与定义其所声明内容的同名源文件之间可以直接相互切换 + +```snap +tests/snap/switch_source_header/switch_source_header/01_header_source/main.cpp +``` + + + + + +**头文件与源文件分开存放** + +`include/` 下的头文件能找到 `src/` 下的源文件,并把它排在另一个同名、但未定义该头文件任何声明的文件之前 + +在头文件所在目录之外,同名文件只有在两者中一方包含另一方时才算数(`tools/shape.cpp` 什么都没有包含,因此不是配对文件),而且其中只取最近的那个:`src/legacy/shape.cpp` 比 `src/shape.cpp` 深一层目录。 + +```snap +tests/snap/switch_source_header/switch_source_header/02_split_layout/main.cpp +``` + + + + + +**定义分散在多个源文件中** + +多个源文件分别定义同一头文件的声明时,直接选中定义数量明显最多的那个,其余的仍留在列表中 + +```snap +tests/snap/switch_source_header/switch_source_header/03_scattered_definitions/main.cpp +``` + + + + + +**没有明确的配对文件** + +没有哪个候选明显胜过其他候选时,编辑器会列出所有候选及其理由,供用户挑选 + +```snap +tests/snap/switch_source_header/switch_source_header/04_split_evenly/main.cpp +``` + + + + + +**模块接口与实现** + +模块接口单元与实现该模块的各个单元配对,每个实现单元也与该接口配对 + +```snap +tests/snap/switch_source_header/switch_source_header/05_module_units/main.cpp +``` + + + + + +**分区与其实现单元** + +内部分区与定义其声明的同名实现单元之间可以直接相互切换 + +该实现单元还会列出其模块的主接口,排在分区之后。 + +```snap +tests/snap/switch_source_header/switch_source_header/06_module_partition/main.cpp +``` + + + + + +**没有配对的文件** + +自身定义了全部所声明内容的头文件,以及 `.def` 片段,都没有配对文件 + +```snap +tests/snap/switch_source_header/switch_source_header/07_no_counterpart/main.cpp +``` + + + + -**源文件与头文件切换** +**模板定义分开存放** -源文件与头文件切换尚未实现,因此用户无法在配对文件之间直接跳转 +头文件与它为存放模板定义而包含的 `.tpp` 文件之间可以相互切换 ```snap -tests/snap/navigation/switch_source_header/01_switch_source_header.cpp +tests/snap/switch_source_header/switch_source_header/08_template_definitions/main.cpp ``` diff --git a/zh/clice/features/overview.md b/zh/clice/features/overview.md index a9f64e777..f8ed7d676 100644 --- a/zh/clice/features/overview.md +++ b/zh/clice/features/overview.md @@ -18,7 +18,7 @@ clice 提供一套基于 LLVM/Clang 构建的 C++ 开发工具。本节介绍已 | [代码补全](./completion.md) | 支持 57 项 | | [悬停](./hover.md) | 支持 38 项 · 部分支持 19 项 · 不支持 10 项 | | [签名帮助](./signature-help.md) | 支持 14 项 | -| [代码导航](./navigation.md) | 支持 71 项 · 部分支持 12 项 · 不支持 26 项 | +| [代码导航](./navigation.md) | 支持 79 项 · 部分支持 12 项 · 不支持 25 项 | | [文档链接](./document-links.md) | 支持 7 项 · 部分支持 1 项 · 不支持 1 项 | | [语义 Token](./semantic-tokens.md) | 支持 56 项 · 部分支持 2 项 · 不支持 10 项 | | [内联提示](./inlay-hints.md) | 支持 34 项 · 部分支持 6 项 · 不支持 4 项 |