Unnamed repository; edit this file 'description' to name the repository.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

| Crate        | Description                                                      |
| -----------  | -----------                                                      |
| helix-core   | Core editing primitives, functional.                             |
| helix-lsp    | Language server client                                           |
| helix-dap    | Debug Adapter Protocol (DAP) client                              |
| helix-loader | Functions for building, fetching, and loading external resources |
| helix-view   | UI abstractions for use in backends, imperative shell.           |
| helix-term   | Terminal UI                                                      |
| helix-tui    | TUI primitives, forked from tui-rs, inspired by Cursive          |


This document contains a high-level overview of Helix internals.

> NOTE: Use `cargo doc --open` for API documentation as well as dependency
> documentation.

## Core

The core contains basic building blocks used to construct the editor. It is
heavily based on [CodeMirror 6](https://codemirror.net/6/docs/). The primitives
are functional: most operations won't modify data in place but instead return
a new copy.

The main data structure used for representing buffers is a `Rope`. We re-export
the excellent [ropey](https://github.com/cessen/ropey) library. Ropes are cheap
to clone, and allow us to easily make snapshots of a text state.

Multiple selections are a core editing primitive. Document selections are
represented by a `Selection`. Each `Range` in the selection consists of a moving
`head` and an immovable `anchor`. A single cursor in the editor is simply
a selection with a single range, with the head and the anchor in the same
position.

Ropes are modified by constructing an OT-like `Transaction`. It represents
a single coherent change to the document and can be applied to the rope.
A transaction can be inverted to produce an undo. Selections and marks can be
mapped over a transaction to translate to a position in the new text state after
applying the transaction.

> NOTE: `Transaction::change`/`Transaction::change_by_selection` is the main
> interface used to generate text edits.

`Syntax` is the interface used to interact with tree-sitter ASTs for syntax
highlighting and other features.

## View

The `view` layer was supposed to be a frontend-agnostic imperative library that
would build on top of `core` to provide the common editor logic. Currently it's
tied to the terminal UI.

A `Document` ties together the `Rope`, `Selection`(s), `Syntax`, document
`History`, language server (etc.) into a comprehensive representation of an open
file.

A `View` represents an open split in the UI. It holds the currently open
document ID and other related state. Views encapsulate the gutter, status line,
diagnostics, and the inner area where the code is displayed.

> NOTE: Multiple views are able to display the same document, so the document
> contains selections for each view. To retrieve, `document.selection()` takes
> a `ViewId`.

`Info` is the autoinfo box that shows hints when awaiting another key with bindings
like `g` and `m`. It is attached to the viewport as a whole.

`Surface` is like a buffer to which widgets draw themselves to, and the
surface is then rendered on the screen on each cycle.

`Rect`s are areas (simply an x and y coordinate with the origin at the
screen top left and then a height and width) which are part of a
`Surface`. They can be used to limit the area to which a `Component` can
render. For example if we wrap a `Markdown` component in a `Popup`
(think the documentation popup with space+k), Markdown's render method
will get a Rect that is the exact size of the popup.

Widgets are called `Component`s internally, and you can see most of them
in `helix-term/src/ui`. Some components like `Popup` and `Overlay` can take
other components as children.

`Layer`s are how multiple components are displayed, and is simply a
`Vec<Component>`. Layers are managed by the `Compositor`. On each top
level render call, the compositor renders each component in the order
they were pushed into the stack. This makes multiple components "layer"
on top of one another. Hence we get a file picker displayed over the
editor, etc.

The `Editor` holds the global state: all the open documents, a tree
representation of all the view splits, the configuration, and a registry of 
language servers. To open or close files, interact with the editor.

## LSP

A language server protocol client.

## Term

The terminal frontend.

The `main` function sets up a new `Application` that runs the event loop.

`commands.rs` is probably the most interesting file. It contains all commands
(actions tied to keybindings). 

`keymap.rs` links commands to key combinations.


## TUI / Term

TODO: document Component and rendering related stuff
n288'>288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
use std::fmt::Display;

use hir::{ModPath, ModuleDef};
use ide_db::{famous_defs::FamousDefs, RootDatabase};
use syntax::{
    ast::{self, HasName},
    AstNode, SyntaxNode,
};

use crate::{
    assist_context::{AssistContext, Assists, SourceChangeBuilder},
    utils::generate_trait_impl_text,
    AssistId, AssistKind,
};

// Assist: generate_deref
//
// Generate `Deref` impl using the given struct field.
//
// ```
// # //- minicore: deref, deref_mut
// struct A;
// struct B {
//    $0a: A
// }
// ```
// ->
// ```
// struct A;
// struct B {
//    a: A
// }
//
// impl core::ops::Deref for B {
//     type Target = A;
//
//     fn deref(&self) -> &Self::Target {
//         &self.a
//     }
// }
// ```
pub(crate) fn generate_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
    generate_record_deref(acc, ctx).or_else(|| generate_tuple_deref(acc, ctx))
}

fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
    let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
    let field = ctx.find_node_at_offset::<ast::RecordField>()?;

    let deref_type_to_generate = match existing_deref_impl(&ctx.sema, &strukt) {
        None => DerefType::Deref,
        Some(DerefType::Deref) => DerefType::DerefMut,
        Some(DerefType::DerefMut) => {
            cov_mark::hit!(test_add_record_deref_impl_already_exists);
            return None;
        }
    };

    let module = ctx.sema.to_def(&strukt)?.module(ctx.db());
    let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?;
    let trait_path =
        module.find_use_path(ctx.db(), ModuleDef::Trait(trait_), ctx.config.prefer_no_std)?;

    let field_type = field.ty()?;
    let field_name = field.name()?;
    let target = field.syntax().text_range();
    acc.add(
        AssistId("generate_deref", AssistKind::Generate),
        format!("Generate `{deref_type_to_generate:?}` impl using `{field_name}`"),
        target,
        |edit| {
            generate_edit(
                ctx.db(),
                edit,
                strukt,
                field_type.syntax(),
                field_name.syntax(),
                deref_type_to_generate,
                trait_path,
            )
        },
    )
}

fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
    let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
    let field = ctx.find_node_at_offset::<ast::TupleField>()?;
    let field_list = ctx.find_node_at_offset::<ast::TupleFieldList>()?;
    let field_list_index = field_list.syntax().children().position(|s| &s == field.syntax())?;

    let deref_type_to_generate = match existing_deref_impl(&ctx.sema, &strukt) {
        None => DerefType::Deref,
        Some(DerefType::Deref) => DerefType::DerefMut,
        Some(DerefType::DerefMut) => {
            cov_mark::hit!(test_add_field_deref_impl_already_exists);
            return None;
        }
    };

    let module = ctx.sema.to_def(&strukt)?.module(ctx.db());
    let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?;
    let trait_path =
        module.find_use_path(ctx.db(), ModuleDef::Trait(trait_), ctx.config.prefer_no_std)?;

    let field_type = field.ty()?;
    let target = field.syntax().text_range();
    acc.add(
        AssistId("generate_deref", AssistKind::Generate),
        format!("Generate `{deref_type_to_generate:?}` impl using `{field}`"),
        target,
        |edit| {
            generate_edit(
                ctx.db(),
                edit,
                strukt,
                field_type.syntax(),
                field_list_index,
                deref_type_to_generate,
                trait_path,
            )
        },
    )
}

fn generate_edit(
    db: &RootDatabase,
    edit: &mut SourceChangeBuilder,
    strukt: ast::Struct,
    field_type_syntax: &SyntaxNode,
    field_name: impl Display,
    deref_type: DerefType,
    trait_path: ModPath,
) {
    let start_offset = strukt.syntax().text_range().end();
    let impl_code = match deref_type {
        DerefType::Deref => format!(
            r#"    type Target = {field_type_syntax};

    fn deref(&self) -> &Self::Target {{
        &self.{field_name}
    }}"#,
        ),
        DerefType::DerefMut => format!(
            r#"    fn deref_mut(&mut self) -> &mut Self::Target {{
        &mut self.{field_name}
    }}"#,
        ),
    };
    let strukt_adt = ast::Adt::Struct(strukt);
    let deref_impl =
        generate_trait_impl_text(&strukt_adt, &trait_path.display(db).to_string(), &impl_code);
    edit.insert(start_offset, deref_impl);
}

fn existing_deref_impl(
    sema: &hir::Semantics<'_, RootDatabase>,
    strukt: &ast::Struct,
) -> Option<DerefType> {
    let strukt = sema.to_def(strukt)?;
    let krate = strukt.module(sema.db).krate();

    let deref_trait = FamousDefs(sema, krate).core_ops_Deref()?;
    let deref_mut_trait = FamousDefs(sema, krate).core_ops_DerefMut()?;
    let strukt_type = strukt.ty(sema.db);

    if strukt_type.impls_trait(sema.db, deref_trait, &[]) {
        if strukt_type.impls_trait(sema.db, deref_mut_trait, &[]) {
            Some(DerefType::DerefMut)
        } else {
            Some(DerefType::Deref)
        }
    } else {
        None
    }
}

#[derive(Debug)]
enum DerefType {
    Deref,
    DerefMut,
}

impl DerefType {
    fn to_trait(
        &self,
        sema: &hir::Semantics<'_, RootDatabase>,
        krate: hir::Crate,
    ) -> Option<hir::Trait> {
        match self {
            DerefType::Deref => FamousDefs(sema, krate).core_ops_Deref(),
            DerefType::DerefMut => FamousDefs(sema, krate).core_ops_DerefMut(),
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable};

    use super::*;

    #[test]
    fn test_generate_record_deref() {
        check_assist(
            generate_deref,
            r#"
//- minicore: deref
struct A { }
struct B { $0a: A }"#,
            r#"
struct A { }
struct B { a: A }

impl core::ops::Deref for B {
    type Target = A;

    fn deref(&self) -> &Self::Target {
        &self.a
    }
}"#,
        );
    }

    #[test]
    fn test_generate_record_deref_short_path() {
        check_assist(
            generate_deref,
            r#"
//- minicore: deref
use core::ops::Deref;
struct A { }
struct B { $0a: A }"#,
            r#"
use core::ops::Deref;
struct A { }
struct B { a: A }

impl Deref for B {
    type Target = A;

    fn deref(&self) -> &Self::Target {
        &self.a
    }
}"#,
        );
    }

    #[test]
    fn test_generate_field_deref_idx_0() {
        check_assist(
            generate_deref,
            r#"
//- minicore: deref
struct A { }
struct B($0A);"#,
            r#"
struct A { }
struct B(A);

impl core::ops::Deref for B {
    type Target = A;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}"#,
        );
    }
    #[test]
    fn test_generate_field_deref_idx_1() {
        check_assist(
            generate_deref,
            r#"
//- minicore: deref
struct A { }
struct B(u8, $0A);"#,
            r#"
struct A { }
struct B(u8, A);

impl core::ops::Deref for B {
    type Target = A;

    fn deref(&self) -> &Self::Target {
        &self.1
    }
}"#,
        );
    }

    #[test]
    fn test_generates_derefmut_when_deref_present() {
        check_assist(
            generate_deref,
            r#"
//- minicore: deref, deref_mut
struct B { $0a: u8 }

impl core::ops::Deref for B {}
"#,
            r#"
struct B { a: u8 }

impl core::ops::DerefMut for B {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.a
    }
}

impl core::ops::Deref for B {}
"#,
        );
    }

    #[test]
    fn test_generate_record_deref_not_applicable_if_already_impl() {
        cov_mark::check!(test_add_record_deref_impl_already_exists);
        check_assist_not_applicable(
            generate_deref,
            r#"
//- minicore: deref, deref_mut
struct A { }
struct B { $0a: A }

impl core::ops::Deref for B {}
impl core::ops::DerefMut for B {}
"#,
        )
    }

    #[test]
    fn test_generate_field_deref_not_applicable_if_already_impl() {
        cov_mark::check!(test_add_field_deref_impl_already_exists);
        check_assist_not_applicable(
            generate_deref,
            r#"
//- minicore: deref, deref_mut
struct A { }
struct B($0A)

impl core::ops::Deref for B {}
impl core::ops::DerefMut for B {}
"#,
        )
    }
}