Skip to content

Commit 2797f48

Browse files
committed
[IMP] handle untitled files
1 parent 8c16351 commit 2797f48

File tree

3 files changed

+453
-132
lines changed

3 files changed

+453
-132
lines changed

server/src/core/entry_point.rs

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub struct EntryPointMgr {
1414
pub main_entry_point: Option<Rc<RefCell<EntryPoint>>>,
1515
pub addons_entry_points: Vec<Rc<RefCell<EntryPoint>>>,
1616
pub custom_entry_points: Vec<Rc<RefCell<EntryPoint>>>,
17+
pub untitled_entry_points: Vec<Rc<RefCell<EntryPoint>>>,
1718
}
1819

1920
impl EntryPointMgr {
@@ -25,8 +26,27 @@ impl EntryPointMgr {
2526
main_entry_point: None,
2627
addons_entry_points: vec![],
2728
custom_entry_points: vec![],
29+
untitled_entry_points: vec![],
2830
}
2931
}
32+
/// Create a new entry for an untitled (in-memory) file.
33+
/// Returns the file symbol for the untitled entry.
34+
pub fn add_entry_to_untitled(session: &mut SessionInfo, path: String) -> Rc<RefCell<Symbol>> {
35+
// For untitled files, we use a minimal tree: just the name as a single OYarn
36+
let tree = vec![OYarn::from(path.clone())];
37+
let entry = EntryPoint::new(
38+
path.clone(),
39+
tree,
40+
EntryPointType::UNTITLED,
41+
None,
42+
None,
43+
);
44+
session.sync_odoo.entry_point_mgr.borrow_mut().untitled_entry_points.push(entry.clone());
45+
// Create one file symbol under the root for the untitled file
46+
let name: String = PathBuf::from(&path).with_extension("").components().last().unwrap().as_os_str().to_str().unwrap().to_string();
47+
let file_sym = entry.borrow().root.borrow_mut().add_new_file(session, &name, &path);
48+
file_sym.clone()
49+
}
3050

3151
/**
3252
* Create each required directory symbols for a given path.
@@ -193,6 +213,13 @@ impl EntryPointMgr {
193213
true
194214
}
195215

216+
pub fn create_new_untitled_entry_for_path(session: &mut SessionInfo, file_name: &String) -> bool {
217+
let new_sym = EntryPointMgr::add_entry_to_untitled(session, file_name.clone());
218+
new_sym.borrow_mut().as_file_mut().self_import = true;
219+
SyncOdoo::add_to_rebuild_arch(session.sync_odoo, new_sym);
220+
true
221+
}
222+
196223
pub fn iter_for_import(&self, current_entry: &Rc<RefCell<EntryPoint>>) -> Box<dyn Iterator<Item = &Rc<RefCell<EntryPoint>>> + '_> {
197224
let mut is_main = false;
198225
for entry in self.iter_main() {
@@ -214,12 +241,12 @@ impl EntryPointMgr {
214241
}
215242

216243
pub fn iter_all(&self) -> impl Iterator<Item = &Rc<RefCell<EntryPoint>>> {
217-
self.addons_entry_points.iter().chain(
218-
self.main_entry_point.iter()).chain(
219-
self.builtins_entry_points.iter()).chain(
220-
self.public_entry_points.iter()).chain(
221-
self.custom_entry_points.iter()
222-
)
244+
self.addons_entry_points.iter()
245+
.chain(self.main_entry_point.iter())
246+
.chain(self.builtins_entry_points.iter())
247+
.chain(self.public_entry_points.iter())
248+
.chain(self.custom_entry_points.iter())
249+
.chain(self.untitled_entry_points.iter())
223250
}
224251

225252
//iter through all main entry points, sorted by tree length (from bigger to smaller)
@@ -256,6 +283,15 @@ impl EntryPointMgr {
256283
self.clean_entries();
257284
}
258285

286+
pub fn remove_untitled_entries_with_path(&mut self, path: &String) {
287+
for entry in self.untitled_entry_points.iter() {
288+
if &entry.borrow().path.clone() == path {
289+
entry.borrow_mut().to_delete = true;
290+
}
291+
}
292+
self.clean_entries();
293+
}
294+
259295
pub fn check_custom_entry_to_delete_with_path(&mut self, path: &String) {
260296
for entry in self.custom_entry_points.iter() {
261297
if entry.borrow().path == *path {
@@ -292,7 +328,7 @@ impl EntryPointMgr {
292328
entry_index += 1;
293329
}
294330
}
295-
let mut entry_index = 0;
331+
entry_index = 0;
296332
while entry_index < self.custom_entry_points.len() {
297333
let entry = self.custom_entry_points[entry_index].clone();
298334
if entry.borrow().to_delete {
@@ -302,6 +338,16 @@ impl EntryPointMgr {
302338
entry_index += 1;
303339
}
304340
}
341+
entry_index = 0;
342+
while entry_index < self.untitled_entry_points.len() {
343+
let entry = self.untitled_entry_points[entry_index].clone();
344+
if entry.borrow().to_delete {
345+
info!("Dropping untitled entry point {}", entry.borrow().path);
346+
self.untitled_entry_points.remove(entry_index);
347+
} else {
348+
entry_index += 1;
349+
}
350+
}
305351
}
306352

307353
/// Transform the path of an addon to the odoo relative path.
@@ -323,7 +369,8 @@ pub enum EntryPointType {
323369
BUILTIN,
324370
PUBLIC,
325371
ADDON,
326-
CUSTOM
372+
CUSTOM,
373+
UNTITLED,
327374
}
328375

329376
#[derive(Debug, Clone)]
@@ -359,6 +406,9 @@ impl EntryPoint {
359406
}
360407

361408
pub fn is_valid_for(&self, path: &PathBuf) -> bool {
409+
if self.typ == EntryPointType::UNTITLED {
410+
return false;
411+
}
362412
path.starts_with(&self.path)
363413
}
364414

server/src/core/file_mgr.rs

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use ropey::Rope;
33
use ruff_python_ast::{ModModule, PySourceType, Stmt};
44
use ruff_python_parser::{Parsed, Token, TokenKind};
55
use lsp_types::{Diagnostic, DiagnosticSeverity, MessageType, NumberOrString, Position, PublishDiagnosticsParams, Range, TextDocumentContentChangeEvent};
6-
use tracing::{error, warn};
6+
use tracing::{info, error, warn};
77
use std::collections::hash_map::DefaultHasher;
88
use std::collections::HashSet;
99
use std::hash::{Hash, Hasher};
@@ -104,9 +104,9 @@ impl FileInfo {
104104
diagnostic_filters: Vec::new(),
105105
}
106106
}
107-
pub fn update(&mut self, session: &mut SessionInfo, uri: &str, content: Option<&Vec<TextDocumentContentChangeEvent>>, version: Option<i32>, in_workspace: bool, force: bool) -> bool {
107+
pub fn update(&mut self, session: &mut SessionInfo, path: &str, content: Option<&Vec<TextDocumentContentChangeEvent>>, version: Option<i32>, in_workspace: bool, force: bool, is_untitled: bool) -> bool {
108108
// update the file info with the given information.
109-
// uri: indicates the path of the file
109+
// path: indicates the path of the file
110110
// content: if content is given, it will be used to update the ast and text_rope, if not, the loading will be from the disk
111111
// version: if the version is provided, the file_info wil be updated only if the new version is higher.
112112
// -100 can be given as version number to indicates that the file has not been opened yet, and that we have to load it ourself
@@ -137,13 +137,16 @@ impl FileInfo {
137137
for change in content.iter() {
138138
self.apply_change(change);
139139
}
140+
} else if is_untitled {
141+
session.log_message(MessageType::ERROR, format!("Attempt to update untitled file {}, without changes", path));
142+
return false;
140143
} else {
141-
match fs::read_to_string(uri) {
144+
match fs::read_to_string(path) {
142145
Ok(content) => {
143146
self.file_info_ast.borrow_mut().text_rope = Some(ropey::Rope::from(content.as_str()));
144147
},
145148
Err(e) => {
146-
session.log_message(MessageType::ERROR, format!("Failed to read file {}, with error {}", uri, e));
149+
session.log_message(MessageType::ERROR, format!("Failed to read file {}, with error {}", path, e));
147150
return false;
148151
},
149152
};
@@ -441,6 +444,7 @@ impl FileInfo {
441444
#[derive(Debug)]
442445
pub struct FileMgr {
443446
pub files: HashMap<String, Rc<RefCell<FileInfo>>>,
447+
untitled_files: HashMap<String, Rc<RefCell<FileInfo>>>, // key: untitled URI or unique name
444448
workspace_folders: HashMap<String, String>,
445449
has_repeated_workspace_folders: bool,
446450
}
@@ -450,6 +454,7 @@ impl FileMgr {
450454
pub fn new() -> Self {
451455
Self {
452456
files: HashMap::new(),
457+
untitled_files: HashMap::new(),
453458
workspace_folders: HashMap::new(),
454459
has_repeated_workspace_folders: false,
455460
}
@@ -463,17 +468,30 @@ impl FileMgr {
463468
}
464469

465470
pub fn get_file_info(&self, path: &String) -> Option<Rc<RefCell<FileInfo>>> {
466-
self.files.get(path).cloned()
471+
if Self::is_untitled(path) {
472+
self.untitled_files.get(path).cloned()
473+
} else {
474+
self.files.get(path).cloned()
475+
}
467476
}
468477

469478
pub fn text_range_to_range(&self, session: &mut SessionInfo, path: &String, range: &TextRange) -> Range {
470-
let file = self.files.get(path);
479+
let file = if Self::is_untitled(path) {
480+
self.untitled_files.get(path)
481+
} else {
482+
self.files.get(path)
483+
};
471484
if let Some(file) = file {
472485
if file.borrow().file_info_ast.borrow().text_rope.is_none() {
473486
file.borrow_mut().prepare_ast(session);
474487
}
475488
return file.borrow().text_range_to_range(range);
476489
}
490+
// For untitled, never try to read from disk
491+
if Self::is_untitled(path) {
492+
session.log_message(MessageType::ERROR, format!("Untitled file {} not found in memory", path));
493+
return Range::default();
494+
}
477495
//file not in cache, let's load rope on the fly
478496
match fs::read_to_string(path) {
479497
Ok(content) => {
@@ -490,13 +508,22 @@ impl FileMgr {
490508

491509

492510
pub fn std_range_to_range(&self, session: &mut SessionInfo, path: &String, range: &std::ops::Range<usize>) -> Range {
493-
let file = self.files.get(path);
511+
let file = if Self::is_untitled(path) {
512+
self.untitled_files.get(path)
513+
} else {
514+
self.files.get(path)
515+
};
494516
if let Some(file) = file {
495517
if file.borrow().file_info_ast.borrow().text_rope.is_none() {
496518
file.borrow_mut().prepare_ast(session);
497519
}
498520
return file.borrow().std_range_to_range(range);
499521
}
522+
// For untitled, never try to read from disk
523+
if Self::is_untitled(path) {
524+
session.log_message(MessageType::ERROR, format!("Untitled file {} not found in memory", path));
525+
return Range::default();
526+
}
500527
//file not in cache, let's load rope on the fly
501528
match fs::read_to_string(path) {
502529
Ok(content) => {
@@ -511,8 +538,20 @@ impl FileMgr {
511538
Range::default()
512539
}
513540

541+
/// Returns true if the path/uri is an untitled (in-memory) file.
542+
/// by convention, untitled files start with "untitled:".
543+
pub fn is_untitled(path: &str) -> bool {
544+
path.starts_with("untitled:")
545+
}
546+
514547
pub fn update_file_info(&mut self, session: &mut SessionInfo, uri: &str, content: Option<&Vec<TextDocumentContentChangeEvent>>, version: Option<i32>, force: bool) -> (bool, Rc<RefCell<FileInfo>>) {
515-
let file_info = self.files.entry(uri.to_string()).or_insert_with(|| {
548+
let is_untitled = Self::is_untitled(uri);
549+
let entry = if is_untitled {
550+
self.untitled_files.entry(uri.to_string())
551+
} else {
552+
self.files.entry(uri.to_string())
553+
};
554+
let file_info = entry.or_insert_with(|| {
516555
let mut file_info = FileInfo::new(uri.to_string());
517556
file_info.update_diagnostic_filters(session);
518557
Rc::new(RefCell::new(file_info))
@@ -522,7 +561,7 @@ impl FileMgr {
522561
let mut updated: bool = false;
523562
if (version.is_some() && version.unwrap() != -100) || !file_info.borrow().opened || force {
524563
let mut file_info_mut = (*return_info).borrow_mut();
525-
updated = file_info_mut.update(session, uri, content, version, self.is_in_workspace(uri), force);
564+
updated = file_info_mut.update(session, uri, content, version, self.is_in_workspace(uri), force, is_untitled);
526565
drop(file_info_mut);
527566
}
528567
(updated, return_info)
@@ -612,13 +651,18 @@ impl FileMgr {
612651
}
613652

614653
pub fn pathname2uri(s: &String) -> lsp_types::Uri {
615-
let mut slash = "";
616-
if cfg!(windows) {
617-
slash = "/";
618-
}
619-
let pre_uri = match url::Url::parse(&format!("file://{}{}", slash, s)) {
620-
Ok(pre_uri) => pre_uri,
621-
Err(err) => panic!("unable to transform pathname to uri: {s}, {}", err)
654+
let pre_uri = if s.starts_with("untitled:"){
655+
s.clone()
656+
} else {
657+
let mut slash = "";
658+
if cfg!(windows) {
659+
slash = "/";
660+
}
661+
let pre_uri = match url::Url::parse(&format!("file://{}{}", slash, s)) {
662+
Ok(pre_uri) => pre_uri,
663+
Err(err) => panic!("unable to transform pathname to uri: {s}, {}", err)
664+
};
665+
pre_uri.to_string()
622666
};
623667
match lsp_types::Uri::from_str(pre_uri.as_str()) {
624668
Ok(url) => url,

0 commit comments

Comments
 (0)