Skip to main content

ktrs_parser/parsing/
kotlin_parser.rs

1//! Port of `KotlinParser.java` plus the chameleon (`ILazyParseableElementType.parseContents`)
2//! reparse used when building trees.
3
4use ktrs_syntax::{Parse, SyntaxKind};
5
6use super::Parser;
7use crate::FileKind;
8use crate::builder::{ChameleonCache, LazyLeaf, PsiBuilder, SemanticWhitespaceAwarePsiBuilder, TreeSink};
9
10/// `KotlinParser.parse(psiBuilder, psiFile)`: `parseFile` or `parseScript` by file kind.
11pub fn parse(text: &str, kind: FileKind) -> Parse {
12    run_top_level(text, |kt_parsing| parse_by_kind(kt_parsing, kind))
13}
14
15/// [`parse`], reusing and extending `cache`'s expanded chameleons.
16pub fn parse_cached(text: &str, kind: FileKind, cache: &mut ChameleonCache) -> Parse {
17    let psi = PsiBuilder::lex_kotlin(text);
18    let mut sink = TreeSink::for_file(&psi, Some(std::mem::take(cache)));
19    run_into(psi, |kt_parsing| parse_by_kind(kt_parsing, kind), None, &mut sink);
20    *cache = sink.take_cache().expect("sink keeps its cache");
21    sink.finish()
22}
23
24fn parse_by_kind(kt_parsing: &mut Parser, kind: FileKind) {
25    match kind {
26        FileKind::Source => kt_parsing.parse_file(),
27        FileKind::Script => kt_parsing.parse_script(),
28    }
29}
30
31pub fn parse_type_code_fragment(text: &str) -> Parse {
32    run_top_level(text, Parser::parse_type_code_fragment)
33}
34
35pub fn parse_expression_code_fragment(text: &str) -> Parse {
36    run_top_level(text, Parser::parse_expression_code_fragment)
37}
38
39pub fn parse_block_code_fragment(text: &str) -> Parse {
40    run_top_level(text, Parser::parse_block_code_fragment)
41}
42
43/// `LambdaExpressionElementType.parseContents`: the reparse of a collapsed `LAMBDA_EXPRESSION`.
44pub fn parse_lambda_expression(text: &str) -> Parse {
45    run_top_level(text, Parser::parse_lambda_expression)
46}
47
48/// `BlockExpressionElementType.parseContents`: the reparse of a collapsed `BLOCK`.
49pub fn parse_block_expression(text: &str) -> Parse {
50    run_top_level(text, Parser::parse_block_expression)
51}
52
53/// `KotlinParsing.createForTopLevel(new SemanticWhitespaceAwarePsiBuilderImpl(psiBuilder))`, run,
54/// then `psiBuilder.getTreeBuilt()`.
55fn run_top_level(text: &str, parse: impl FnOnce(&mut Parser)) -> Parse {
56    let psi = PsiBuilder::lex_kotlin(text);
57    let mut sink = TreeSink::for_file(&psi, None);
58    run_into(psi, parse, None, &mut sink);
59    sink.finish()
60}
61
62fn run_into(psi: PsiBuilder, parse: impl FnOnce(&mut Parser), root_kind: Option<SyntaxKind>, sink: &mut TreeSink) {
63    let mut kt_parsing = Parser::create_for_top_level(SemanticWhitespaceAwarePsiBuilder::new(psi));
64    parse(&mut kt_parsing);
65    kt_parsing.my_builder.psi.build_tree_into(root_kind, sink, &reparse_lazy);
66}
67
68/// Expands a lazy-parseable leaf: its kind's `parseContents` over its own text. The reparsed
69/// root's children become the leaf node's children (`getFirstChildNode()` upstream).
70/// Kotlin's lazy types don't `reuseCollapsedTokens`, so remaps made by the outer parse are
71/// forgotten; the leaf's unremapped lexemes stand in for re-lexing its text (see `LazyLeaf`).
72pub(crate) fn reparse_lazy(leaf: &LazyLeaf<'_>, sink: &mut TreeSink) -> bool {
73    let (kind, text) = (leaf.kind, leaf.text);
74    match kind {
75        SyntaxKind::BLOCK => sink.chameleon(kind, text, |sink| {
76            run_into(leaf.relexed_builder(), Parser::parse_block_expression, Some(kind), sink)
77        }),
78        SyntaxKind::LAMBDA_EXPRESSION => sink.chameleon(kind, text, |sink| {
79            run_into(leaf.relexed_builder(), Parser::parse_lambda_expression, Some(kind), sink)
80        }),
81        SyntaxKind::DOC_COMMENT => sink.chameleon(kind, text, |sink| crate::kdoc::parse_kdoc_into(text, sink)),
82        SyntaxKind::KDOC_MARKDOWN_LINK => {
83            sink.chameleon(kind, text, |sink| crate::kdoc::parse_markdown_link_into(text, sink))
84        }
85        _ => return false,
86    }
87    true
88}