Fork of daniellemaywood.uk/gleam — Wasm codegen work
5.1 kB
195 lines
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2023 The Gleam contributors
3
4#![warn(
5 clippy::all,
6 clippy::redundant_clone,
7 clippy::dbg_macro,
8 clippy::todo,
9 clippy::mem_forget,
10 clippy::filter_map_next,
11 clippy::needless_continue,
12 clippy::needless_borrow,
13 clippy::match_wildcard_for_single_variants,
14 clippy::imprecise_flops,
15 clippy::suboptimal_flops,
16 clippy::lossy_float_literal,
17 clippy::rest_pat_in_fully_bound_structs,
18 clippy::fn_params_excessive_bools,
19 clippy::inefficient_to_string,
20 clippy::linkedlist,
21 clippy::macro_use_imports,
22 clippy::option_option,
23 clippy::verbose_file_reads,
24 clippy::unnested_or_patterns,
25 clippy::default_trait_access,
26 rust_2018_idioms,
27 missing_debug_implementations,
28 missing_copy_implementations,
29 trivial_casts,
30 trivial_numeric_casts,
31 nonstandard_style,
32 unexpected_cfgs,
33 unused_import_braces,
34 unused_qualifications
35)]
36#![deny(
37 clippy::await_holding_lock,
38 clippy::disallowed_methods,
39 clippy::if_let_mutex,
40 clippy::indexing_slicing,
41 clippy::mem_forget,
42 clippy::ok_expect,
43 clippy::unimplemented,
44 clippy::unwrap_used,
45 unsafe_code,
46 unstable_features,
47 unused_results
48)]
49#![allow(
50 clippy::assign_op_pattern,
51 clippy::to_string_trait_impl,
52 clippy::match_single_binding,
53 clippy::match_like_matches_macro,
54 clippy::inconsistent_struct_constructor,
55 clippy::len_without_is_empty
56)]
57
58mod code_action;
59mod compiler;
60mod completer;
61mod edits;
62mod engine;
63mod feedback;
64mod files;
65mod messages;
66mod progress;
67mod reference;
68mod rename;
69mod router;
70mod server;
71mod signature_help;
72
73#[cfg(test)]
74mod tests;
75
76pub use server::LanguageServer;
77
78use camino::Utf8PathBuf;
79use gleam_core::{
80 Result, ast::SrcSpan, build::Target, line_numbers::LineNumbers, manifest::Manifest,
81 paths::ProjectPaths,
82};
83use lsp_types::{Position, Range, TextEdit, Uri as Url};
84use std::any::Any;
85
86#[derive(Debug)]
87pub struct LockGuard(pub Box<dyn Any>);
88
89pub trait Locker {
90 fn lock_for_build(&self) -> Result<LockGuard>;
91}
92
93pub trait MakeLocker {
94 fn make_locker(&self, paths: &ProjectPaths, target: Target) -> Result<Box<dyn Locker>>;
95}
96
97pub trait DownloadDependencies {
98 fn download_dependencies(&self, paths: &ProjectPaths) -> Result<Manifest>;
99}
100
101pub fn src_span_to_lsp_range(location: SrcSpan, line_numbers: &LineNumbers) -> Range {
102 let start = line_numbers.line_and_column_number(location.start);
103 let end = line_numbers.line_and_column_number(location.end);
104
105 Range::new(
106 Position::new(start.line - 1, start.column - 1),
107 Position::new(end.line - 1, end.column - 1),
108 )
109}
110
111pub fn lsp_range_to_src_span(range: Range, line_numbers: &LineNumbers) -> SrcSpan {
112 let start = line_numbers.byte_index(range.start);
113 let end = line_numbers.byte_index(range.end);
114 SrcSpan { start, end }
115}
116
117/// A little wrapper around LineNumbers to make it easier to build text edits.
118///
119#[derive(Debug)]
120pub struct TextEdits<'a> {
121 line_numbers: &'a LineNumbers,
122 edits: Vec<TextEdit>,
123}
124
125impl<'a> TextEdits<'a> {
126 pub fn new(line_numbers: &'a LineNumbers) -> Self {
127 TextEdits {
128 line_numbers,
129 edits: vec![],
130 }
131 }
132
133 pub fn src_span_to_lsp_range(&self, location: SrcSpan) -> Range {
134 src_span_to_lsp_range(location, self.line_numbers)
135 }
136
137 pub fn lsp_range_to_src_span(&self, range: Range) -> SrcSpan {
138 lsp_range_to_src_span(range, self.line_numbers)
139 }
140
141 pub fn replace(&mut self, location: SrcSpan, new_text: String) {
142 self.edits.push(TextEdit {
143 range: src_span_to_lsp_range(location, self.line_numbers),
144 new_text,
145 })
146 }
147
148 pub fn insert(&mut self, at: u32, new_text: String) {
149 self.replace(SrcSpan { start: at, end: at }, new_text)
150 }
151
152 pub fn delete(&mut self, location: SrcSpan) {
153 self.replace(location, "".to_string())
154 }
155
156 fn delete_range(&mut self, range: Range) {
157 self.edits.push(TextEdit {
158 range,
159 new_text: "".into(),
160 })
161 }
162}
163
164fn path(uri: &Url) -> Utf8PathBuf {
165 // The to_file_path method is available on these platforms
166 #[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))]
167 return Utf8PathBuf::from_path_buf(uri.to_file_path().expect("URL file"))
168 .expect("Non Utf8 Path");
169
170 #[cfg(not(any(unix, windows, target_os = "redox", target_os = "wasi")))]
171 return Utf8PathBuf::from_path_buf(uri.path().into()).expect("Non Utf8 Path");
172}
173
174fn url_from_path(path: &str) -> Option<Url> {
175 // The targets for which `from_file_path` is defined
176 #[cfg(any(
177 unix,
178 windows,
179 target_os = "redox",
180 target_os = "wasi",
181 target_os = "hermit"
182 ))]
183 let uri = Url::from_file_path(path).ok();
184
185 #[cfg(not(any(
186 unix,
187 windows,
188 target_os = "redox",
189 target_os = "wasi",
190 target_os = "hermit"
191 )))]
192 let uri = Url::parse(&format!("file://{path}")).ok();
193
194 uri
195}