1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use chrono::DateTime;
use chrono::Utc;
use deno_core::parking_lot::Mutex;
use std::fs;
use std::io::prelude::*;
use std::path::Path;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::thread;
use std::time::SystemTime;
static LSP_DEBUG_FLAG: AtomicBool = AtomicBool::new(false);
static LSP_LOG_LEVEL: AtomicUsize = AtomicUsize::new(log::Level::Info as usize);
static LSP_WARN_LEVEL: AtomicUsize =
AtomicUsize::new(log::Level::Warn as usize);
static LOG_FILE: LogFile = LogFile {
enabled: AtomicBool::new(true),
buffer: Mutex::new(String::new()),
};
pub struct LogFile {
enabled: AtomicBool,
buffer: Mutex<String>,
}
impl LogFile {
pub fn write_line(&self, s: &str) {
if LOG_FILE.enabled.load(Ordering::Relaxed) {
let mut buffer = self.buffer.lock();
buffer.push_str(s);
buffer.push('\n');
}
}
fn commit(&self, path: &Path) {
let unbuffered = {
let mut buffer = self.buffer.lock();
if buffer.is_empty() {
return;
}
// We clone here rather than take so the buffer can retain its capacity.
let unbuffered = buffer.clone();
buffer.clear();
unbuffered
};
if let Ok(file) = fs::OpenOptions::new().append(true).open(path) {
write!(&file, "{}", unbuffered).ok();
}
}
}
pub fn init_log_file(enabled: bool) {
let prepare_path = || {
if !enabled {
return None;
}
let cwd = std::env::current_dir().ok()?;
let now = SystemTime::now();
let now: DateTime<Utc> = now.into();
let now = now.to_rfc3339().replace(':', "_");
let path = cwd.join(format!(".deno_lsp/log_{}.txt", now));
fs::create_dir_all(path.parent()?).ok()?;
fs::write(&path, "").ok()?;
Some(path)
};
let Some(path) = prepare_path() else {
LOG_FILE.enabled.store(false, Ordering::Relaxed);
LOG_FILE.buffer.lock().clear();
return;
};
thread::spawn(move || loop {
LOG_FILE.commit(&path);
thread::sleep(std::time::Duration::from_secs(1));
});
}
pub fn write_line_to_log_file(s: &str) {
LOG_FILE.write_line(s);
}
pub fn set_lsp_debug_flag(value: bool) {
LSP_DEBUG_FLAG.store(value, Ordering::SeqCst)
}
pub fn lsp_debug_enabled() -> bool {
LSP_DEBUG_FLAG.load(Ordering::SeqCst)
}
/// Change the lsp to log at the provided level.
pub fn set_lsp_log_level(level: log::Level) {
LSP_LOG_LEVEL.store(level as usize, Ordering::SeqCst)
}
pub fn lsp_log_level() -> log::Level {
let level = LSP_LOG_LEVEL.load(Ordering::SeqCst);
// TODO(bartlomieju):
#[allow(clippy::undocumented_unsafe_blocks)]
unsafe {
std::mem::transmute(level)
}
}
/// Change the lsp to warn at the provided level.
pub fn set_lsp_warn_level(level: log::Level) {
LSP_WARN_LEVEL.store(level as usize, Ordering::SeqCst)
}
pub fn lsp_warn_level() -> log::Level {
let level = LSP_LOG_LEVEL.load(Ordering::SeqCst);
// TODO(bartlomieju):
#[allow(clippy::undocumented_unsafe_blocks)]
unsafe {
std::mem::transmute(level)
}
}
/// Use this macro to do "info" logs in the lsp code. This allows
/// for downgrading these logs to another log level in the REPL.
macro_rules! lsp_log {
($($arg:tt)+) => (
let lsp_log_level = $crate::lsp::logging::lsp_log_level();
if lsp_log_level == log::Level::Debug {
$crate::lsp::logging::lsp_debug!($($arg)+)
} else {
let s = std::format!($($arg)+);
$crate::lsp::logging::write_line_to_log_file(&s);
log::log!(lsp_log_level, "{}", s)
}
)
}
/// Use this macro to do "warn" logs in the lsp code. This allows
/// for downgrading these logs to another log level in the REPL.
macro_rules! lsp_warn {
($($arg:tt)+) => (
{
let lsp_log_level = $crate::lsp::logging::lsp_warn_level();
if lsp_log_level == log::Level::Debug {
$crate::lsp::logging::lsp_debug!($($arg)+)
} else {
let s = std::format!($($arg)+);
$crate::lsp::logging::write_line_to_log_file(&s);
log::log!(lsp_log_level, "{}", s)
}
}
)
}
macro_rules! lsp_debug {
($($arg:tt)+) => (
{
let s = std::format!($($arg)+);
$crate::lsp::logging::write_line_to_log_file(&s);
if $crate::lsp::logging::lsp_debug_enabled() {
log::debug!("{}", s)
}
}
)
}
pub(super) use lsp_debug;
pub(super) use lsp_log;
pub(super) use lsp_warn;
|