mirror of
https://github.com/google/gn-language-server.git
synced 2025-12-23 12:26:43 +00:00
Update ARCHITECTURE.md
This commit is contained in:
parent
f20b6aeaa3
commit
8d66390609
1 changed files with 35 additions and 24 deletions
|
|
@ -29,14 +29,21 @@ This is a deliberate trade-off that prioritizes simplicity and a holistic editin
|
|||
- **Ambiguous Results**: LSP features may provide ambiguous results. For example, "Go to Definition" on a variable may navigate to multiple assignments across different conditional blocks.
|
||||
- **Diagnostic Mismatches**: The server's error checking may differ from `gn check`. It might produce false positives for code in an inactive block or miss errors in code that is currently disabled.
|
||||
|
||||
### Two-Tiered Analysis (Shallow vs. Full)
|
||||
### Single-Pass Caching & On-Demand Scope Resolution
|
||||
|
||||
The analyzer uses a two-tiered strategy to balance performance, correctness, and feature richness.
|
||||
The analyzer employs a unified caching strategy combined with on-demand scope construction.
|
||||
|
||||
- **Shallow Analysis**: Performed on imported files (`.gni`). This pass analyzes the entire file, including statements inside conditionals, but crucially does not perform a deep analysis of `template` bodies. It identifies template *definitions* but not their internal logic. This is essential for two reasons:
|
||||
1. **Performance**: It avoids costly, deep analysis of files that are only used for their top-level variables and template definitions.
|
||||
2. **Correctness**: It prevents infinite recursion that could occur if `import` statements inside templates were analyzed without a specific invocation context.
|
||||
- **Full Analysis**: Performed on the primary file being edited (`.gn` or `.gni`). This is a deep, comprehensive pass that builds a complete semantic graph, resolving scopes, variable assignments, and dependencies. This detailed model powers most LSP features like "Go to Definition" and "Hover". The full analyzer leverages the cached results of the shallow analyzer for imported files to maintain performance.
|
||||
- **Per-File Analysis (`AnalyzedFile`)**: Each file (`.gn` or `.gni`) is parsed and analyzed independently to extract local information:
|
||||
- **Abstract Syntax Tree (AST)**: The structural representation of the code.
|
||||
- **Exports**: Variables, templates, and targets defined at the top level.
|
||||
- **Links**: File paths and target labels referenced in the file.
|
||||
- **Symbols**: A simple index of symbols for document outline.
|
||||
This result is wrapped in an `AnalyzedFile` and cached. If a file hasn't changed, this cached result is reused instantly.
|
||||
|
||||
- **On-Demand Scope Building (`analyze_at`)**: When a feature requires a full semantic understanding of a specific location (e.g., "Go to Definition" or "Completion" at a cursor position), the analyzer dynamically constructs an `Environment`.
|
||||
- It starts from the target file and recursively gathers exports from all imported files (transitive imports).
|
||||
- It combines these exports with the local definitions available at that specific position in the code.
|
||||
- This process is fast because it relies on the pre-computed, cached `AnalyzedFile` structures, avoiding the need to re-parse or re-analyze the dependencies.
|
||||
|
||||
### Caching and Performance
|
||||
|
||||
|
|
@ -60,11 +67,11 @@ The server is designed to be mostly standalone but relies on the `gn` command-li
|
|||
|
||||
The server is designed with a modular architecture, separating concerns into distinct components.
|
||||
|
||||
### Server (`server.rs`)
|
||||
### Server (`src/server/mod.rs`)
|
||||
|
||||
This is the main entry point of the application. It initializes the server, manages the LSP request/response lifecycle, and holds the shared state of the application, including the document storage and the analyzer.
|
||||
|
||||
### Document Storage (`storage.rs`)
|
||||
### Document Storage (`src/common/storage.rs`)
|
||||
|
||||
This component acts as a cache for file contents. It distinguishes between:
|
||||
1. Files currently open and being edited in the client (in-memory).
|
||||
|
|
@ -72,26 +79,29 @@ This component acts as a cache for file contents. It distinguishes between:
|
|||
|
||||
It uses a combination of LSP document versions (for in-memory files) and file system modification timestamps (for on-disk files) to determine if a file is "fresh" or needs to be re-read.
|
||||
|
||||
### Parser (`ast/`)
|
||||
### Parser (`src/parser/`)
|
||||
|
||||
The parser is responsible for turning raw text into a structured representation.
|
||||
- **Grammar (`gn.pest`)**: A formal grammar defines the syntax of the GN language. This makes the parser predictable and easy to maintain.
|
||||
- **AST (`ast/mod.rs`, `ast/parser.rs`)**: The raw parse tree from `pest` is transformed into a more ergonomic Abstract Syntax Tree (AST). The AST nodes provide methods for easy traversal and inspection, forming the input for the semantic analyzer.
|
||||
- **Grammar (`src/parser/gn.pest`)**: A formal grammar defines the syntax of the GN language. This makes the parser predictable and easy to maintain.
|
||||
- **AST (`src/parser/mod.rs`, `src/parser/parse.rs`)**: The raw parse tree from `pest` is transformed into a more ergonomic Abstract Syntax Tree (AST). The AST nodes provide methods for easy traversal and inspection, forming the input for the semantic analyzer.
|
||||
|
||||
### Semantic Analyzer (`analyze/`)
|
||||
### Semantic Analyzer (`src/analyzer/`)
|
||||
|
||||
The analyzer is the brain of the language server. It consumes the AST and builds a rich semantic understanding of the code.
|
||||
|
||||
- **Workspace Context**: The server establishes the workspace context by first finding the root directory, identified by a `.gn` file. This root path is essential for resolving source-absolute paths (e.g., `//path/to/file.cc`). The server then parses the `.gn` file to locate the main `buildconfig` file, which serves as the entry point for analyzing the build graph and understanding the default configuration.
|
||||
- **Workspace Context**: The server establishes the workspace context by first finding the root directory, identified by a `.gn` file. This root path is essential for resolving source-absolute paths (e.g., `//path/to/file.cc`). The `WorkspaceAnalyzer` manages the state for a specific workspace, including the build configuration loaded from `build/config/BUILDCONFIG.gn`.
|
||||
|
||||
- **Key Data Structures**:
|
||||
- `AnalyzedFile`: The complete semantic model for a single file.
|
||||
- `AnalyzedEvent`: An enum representing a semantically significant occurrence in a file, such as an assignment, import, or target definition. The full analyzer's output is a stream of these events.
|
||||
- `AnalyzedScope`: Represents a lexical scope as a tree, mapping variable names to their definitions and linking to parent scopes.
|
||||
- `AnalyzedTarget`, `AnalyzedTemplate`: Represent defined targets and templates.
|
||||
- `Link`: Represents a semantic connection from one file to another, such as a file path in a string or a target label in a `deps` list.
|
||||
- `AnalyzedFile`: The complete, cached semantic model for a single file, containing its AST, exports, and links.
|
||||
- `Environment`: Represents the fully resolved scope at a specific point in execution, aggregating variables and templates from the current file and all its dependencies.
|
||||
- `FileExports`: Summarizes the public interface of a file (variables, templates, targets) available to importers.
|
||||
- `AnalyzedBlock`, `AnalyzedStatement`: Semantic wrappers around AST nodes, holding resolved scopes and other metadata.
|
||||
|
||||
### LSP Feature Providers (`providers/`)
|
||||
- **Analysis Flow**:
|
||||
- `analyze_file(path)`: Returns the cached `AnalyzedFile`.
|
||||
- `analyze_at(file, pos)`: Returns an `Environment` representing the state of the program at `pos`, aggregating definitions from the build config and imports.
|
||||
|
||||
### LSP Feature Providers (`src/server/providers/`)
|
||||
|
||||
Each LSP feature is implemented in its own module. These providers consume the data from the Semantic Analyzer to generate responses for the client. Examples include `completion`, `hover`, `goto_definition`, and `references`.
|
||||
|
||||
|
|
@ -100,8 +110,9 @@ Each LSP feature is implemented in its own module. These providers consume the d
|
|||
A typical request flows through the system as follows:
|
||||
|
||||
1. **Request**: The user triggers "Go to Definition" on a variable in the editor. The client sends a `textDocument/definition` request to the server.
|
||||
2. **Dispatch**: `server.rs` receives the request and dispatches it to the `goto_definition` provider.
|
||||
3. **Analysis**: The provider requests the `AnalyzedFile` for the current document from the `Analyzer`.
|
||||
4. **Cache Check**: The `Analyzer` checks its cache for a fresh `AnalyzedFile`. If it's stale, it re-analyzes the file. This full analysis in turn uses the `ShallowAnalyzer` for any imported `.gni` files, which has its own cache of `ShallowAnalyzedFile` objects. This two-level caching ensures that only the necessary files are re-parsed.
|
||||
5. **Resolution**: The `goto_definition` provider inspects the `AnalyzedFile`. It finds the identifier under the cursor, looks up its definition within the current scope, and finds the location of the original assignment.
|
||||
6. **Response**: The provider constructs a `LocationLink` response containing the URI and position of the definition and returns it to the client, which then moves the user's cursor to the correct location.
|
||||
2. **Dispatch**: The `Backend` in `src/server/mod.rs` receives the request and dispatches it to the `goto_definition` provider.
|
||||
3. **Analysis (File)**: The provider calls `analyzer.analyze_file()` to get the `AnalyzedFile` for the current document. This returns the cached result of the local analysis (AST, links, exports).
|
||||
4. **Link Check**: The provider first checks if the cursor is on a "link" (e.g., a file path in an import or a target label in `deps`). If so, it resolves the destination immediately.
|
||||
5. **Analysis (Scope)**: If the cursor is on an identifier, the provider calls `analyzer.analyze_at()`. This triggers the on-demand scope construction, aggregating exports from imported files to build a complete `Environment` for that specific position.
|
||||
6. **Resolution**: The provider looks up the identifier in the `Environment` to find all matching variable assignments or template definitions.
|
||||
7. **Response**: The provider constructs a `LocationLink` response containing the URIs and ranges of the definitions and returns it to the client.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue