Файли
openidea/crates/ide-lsp/src/virtual_doc.rs
T

110 рядки
4.3 KiB
Rust
Неформатований Анотація Історія

Цей файл містить неоднозначні символи Юнікоду
Цей файл містить символи Юнікоду, які можна сплутати з іншими символами. Якщо ви вважаєте, що це зроблено навмисно, можете сміливо ігнорувати це попередження. Щоб показати їх, скористайтеся кнопкою Escape.
//! Virtual documents: read-only contents a language server provides for a
//! non-`file:` URI (library classes from `jdt://`, `workspace/textDocumentContent`).
use percent_encoding::percent_decode_str;
/// Markers decompilers / disassemblers put at the top of generated code.
const GENERATED_MARKERS: [&str; 5] = [
"generated by the fernflower decompiler",
"failed to get sources",
"decompiled with cfr",
"decompiled with procyon",
"generated by the disassembler",
];
/// Whether `text` is decompiled / disassembled code rather than real source.
pub fn is_decompiled(text: &str) -> bool {
let head: String = text.chars().take(600).collect::<String>().to_ascii_lowercase();
GENERATED_MARKERS.iter().any(|m| head.contains(m))
}
/// `scheme` of a URI (`jdt` for `jdt://…`).
pub fn scheme(uri: &str) -> &str {
uri.split_once(':').map(|(s, _)| s).unwrap_or("")
}
/// The URI's path segments (without query / fragment), percent-decoded.
fn segments(uri: &str) -> Vec<String> {
let rest = uri.split_once(':').map(|(_, r)| r).unwrap_or(uri);
let rest = rest.split(['?', '#']).next().unwrap_or(rest);
let rest = rest.strip_prefix("//").unwrap_or(rest);
rest.split('/').filter(|s| !s.is_empty()).map(|s| percent_decode_str(s).decode_utf8_lossy().into_owned()).collect()
}
/// Editor tab title: the last path segment; a class file whose real source
/// was found is shown as `.java` (IDEA: `String.java` vs `String.class`).
pub fn title(uri: &str, decompiled: bool) -> String {
let name = segments(uri).pop().unwrap_or_else(|| uri.to_owned());
match name.strip_suffix(".class") {
Some(stem) if !decompiled && scheme(uri) == "jdt" => format!("{stem}.java"),
_ => name,
}
}
/// Where the document comes from, e.g. the jar path of a `jdt://` class.
pub fn description(uri: &str) -> Option<String> {
if scheme(uri) != "jdt" {
return None;
}
let segs = segments(uri);
// jdt://contents/<jar or module>/<package>/<Class.class>?=<project>/<escaped jar path><package(Class.class
let container = segs.get(1).cloned();
let query = uri.split_once('?').map(|(_, q)| q).unwrap_or("");
let query = percent_decode_str(query).decode_utf8_lossy();
let jar = query
.split_once('/')
.map(|(_, rest)| rest.replace('\\', ""))
.and_then(|rest| rest.split(['<', '(', '`', '=']).next().map(str::to_owned))
.filter(|p| p.ends_with(".jar") || p.ends_with(".jmod"));
let package = segs.get(2).filter(|_| segs.len() >= 4).cloned();
match (jar, container) {
(Some(jar), _) if jar.ends_with("jrt-fs.jar") => segs.get(1).cloned(),
(Some(jar), _) => Some(jar),
(None, Some(c)) => Some(match package {
Some(p) => format!("{c} › {p}"),
None => c,
}),
(None, None) => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
const JDT: &str = "jdt://contents/spring-boot-4.1.1.jar/org.springframework.boot/SpringApplication.class?=spring-demo/%5C/Users%5C/me%5C/.m2%5C/repository%5C/spring-boot-4.1.1.jar%3Corg.springframework.boot%28SpringApplication.class";
#[test]
fn jdt_titles() {
assert_eq!(title(JDT, true), "SpringApplication.class");
assert_eq!(title(JDT, false), "SpringApplication.java");
assert_eq!(title("untitled:foo/bar.txt", false), "bar.txt");
assert_eq!(scheme(JDT), "jdt");
}
#[test]
fn jdt_description() {
assert_eq!(description(JDT).as_deref(), Some("/Users/me/.m2/repository/spring-boot-4.1.1.jar"));
assert_eq!(
description(
"jdt://contents/java.base/java.lang/String.class?=p/%5C/jdk%5C/lib%5C/jrt-fs.jar%60java.base=x"
)
.as_deref(),
Some("java.base")
);
assert_eq!(description("jdt://contents/a.jar/p/C.class").as_deref(), Some("a.jar › p"));
assert_eq!(description("foo://x/y"), None);
}
#[test]
fn detects_generated_code() {
assert!(is_decompiled(
"// Source code is unavailable, and was generated by the Fernflower decompiler.\npackage a;"
));
assert!(is_decompiled(
"// Failed to get sources. Instead, stub sources have been generated by the disassembler."
));
assert!(!is_decompiled("/*\n * Copyright 2012-2024 the original author or authors.\n */\npackage a;"));
}
}