#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
use super::*;
fn kotlin_leading_identifier_len(text: &str) -> usize {
let mut chars = text.char_indices();
let Some((_, first)) = chars.next() else {
return 0;
};
if first != '_' && !first.is_alphabetic() {
return 0;
}
let mut end = first.len_utf8();
for (idx, c) in chars {
if c == '_' || c.is_alphanumeric() {
end = idx + c.len_utf8();
} else {
break;
}
}
end
}
fn kotlin_is_short_interp_name(node: &Node, code: &[u8]) -> bool {
use Kotlin::{StringContent, StringContent2, StringContent3};
const STRING_CONTENT_KINDS: [u16; 3] = [
StringContent as u16,
StringContent2 as u16,
StringContent3 as u16,
];
if !STRING_CONTENT_KINDS.contains(&node.kind_id()) {
return false;
}
let Some(prev) = node.previous_sibling() else {
return false;
};
if !STRING_CONTENT_KINDS.contains(&prev.kind_id()) || prev.utf8_text(code) != Some("$") {
return false;
}
node.utf8_text(code)
.is_some_and(|text| kotlin_leading_identifier_len(text) > 0)
}
fn kotlin_short_interp_operand_bytes<'a>(node: &Node, code: &'a [u8]) -> &'a [u8] {
let start = node.start_byte();
let end = node.end_byte();
let full = &code[start..end];
match std::str::from_utf8(full) {
Ok(text) => {
let prefix_len = kotlin_leading_identifier_len(text);
if prefix_len > 0 {
&full[..prefix_len]
} else {
full
}
}
Err(_) => full,
}
}
fn kotlin_string_has_interp(node: &Node, code: &[u8]) -> bool {
use Kotlin::Interpolation;
node.children().any(|child| {
child.kind_id() == Interpolation as u16 || kotlin_is_short_interp_name(&child, code)
})
}
impl Getter for KotlinCode {
fn get_space_kind(node: &Node) -> SpaceKind {
use Kotlin::*;
match node.kind_id().into() {
ClassDeclaration => {
if node.first_child(|id| id == Interface).is_some() {
SpaceKind::Interface
} else {
SpaceKind::Class
}
}
ObjectDeclaration | CompanionObject | ObjectLiteral => SpaceKind::Class,
FunctionDeclaration | SecondaryConstructor | LambdaLiteral | AnonymousFunction => {
SpaceKind::Function
}
SourceFile => SpaceKind::Unit,
_ => SpaceKind::Unknown,
}
}
fn get_op_type(node: &Node) -> HalsteadType {
use Kotlin::*;
match node.kind_id().into() {
If | Else | When | For | While | Do | Try | Catch | Finally | Throw | Return
| ReturnAT
| Class | Fun | Object | Val | Var | In | Is | As | AsQMARK | BANGis | BANGin
| This | Super | Constructor
| SEMI | COMMA | COLONCOLON | DOT | LBRACE | LBRACK | LPAREN
| EQ | PLUS | DASH | STAR | SLASH | PERCENT
| PLUSEQ | DASHEQ | STAREQ | SLASHEQ | PERCENTEQ
| PLUSPLUS | DASHDASH
| LT | GT | LTEQ | GTEQ | EQEQ | EQEQEQ | BANGEQ | BANGEQEQ
| AMPAMP | PIPEPIPE | BANG | BANGBANG
| QMARK | QMARKCOLON | QMARKDOT
| DOTDOT | DOTDOTLT | DASHGT | COLON => HalsteadType::Operator,
Identifier | NumberLiteral | FloatLiteral | CharacterLiteral | Label => {
HalsteadType::Operand
}
StringLiteral | MultilineStringLiteral => {
Self::string_operand_type(node, &[Interpolation as u16])
}
_ => HalsteadType::Unknown,
}
}
fn get_op_type_with_code(node: &Node, code: &[u8]) -> HalsteadType {
use Kotlin::*;
match node.kind_id().into() {
StringContent | StringContent2 | StringContent3
if kotlin_is_short_interp_name(node, code) =>
{
HalsteadType::Operand
}
StringLiteral | MultilineStringLiteral => {
if kotlin_string_has_interp(node, code) {
HalsteadType::Unknown
} else {
HalsteadType::Operand
}
}
_ => Self::get_op_type(node),
}
}
fn get_operand_id<'a>(node: &Node, code: &'a [u8]) -> &'a [u8] {
if kotlin_is_short_interp_name(node, code) {
kotlin_short_interp_operand_bytes(node, code)
} else {
&code[node.start_byte()..node.end_byte()]
}
}
get_operator!(Kotlin);
}