дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-08 19:02:17 +02:00
110 рядки
4.3 KiB
Rust
110 рядки
4.3 KiB
Rust
//! 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;"));
|
||
}
|
||
}
|