Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions crates/emmylua_check/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ use emmylua_code_analysis::{
};
use fern::Dispatch;
use log::LevelFilter;
use std::{
path::{Path, PathBuf},
str::FromStr,
};
use std::path::{Path, PathBuf};

fn root_from_configs(config_paths: &[PathBuf], fallback: &Path) -> PathBuf {
if config_paths.len() != 1 {
Expand Down Expand Up @@ -102,12 +99,12 @@ pub fn load_workspace(
}

for root in &emmyrc.workspace.workspace_roots {
analysis.add_main_workspace(PathBuf::from_str(root).unwrap());
analysis.add_main_workspace(PathBuf::from(root));
}

for lib in &emmyrc.workspace.library {
analysis.add_library_workspace(PathBuf::from_str(lib).unwrap());
workspace_folders.push(PathBuf::from_str(lib).unwrap());
analysis.add_library_workspace(PathBuf::from(lib));
workspace_folders.push(PathBuf::from(lib));
}

let file_infos = collect_files(&workspace_folders, &analysis.emmyrc, ignore);
Expand Down
9 changes: 9 additions & 0 deletions crates/emmylua_code_analysis/resources/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@
"ignoreGlobs": [],
"library": [],
"moduleMap": [],
"packageDirs": [],
"preloadFileSize": 0,
"reindexDuration": 5000,
"workspaceRoots": []
Expand Down Expand Up @@ -1100,6 +1101,14 @@
"$ref": "#/$defs/EmmyrcWorkspaceModuleMap"
}
},
"packageDirs": {
"description": "Package directories. Treat the parent directory as a `library`, but only add files from the specified directory.\neg: `/usr/local/share/lua/5.1/module`",
"type": "array",
"default": [],
"items": {
"type": "string"
}
},
"preloadFileSize": {
"type": "integer",
"format": "int32",
Expand Down
5 changes: 5 additions & 0 deletions crates/emmylua_code_analysis/src/config/configs/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ pub struct EmmyrcWorkspace {
/// Library paths. eg: "/usr/local/share/lua/5.1"
pub library: Vec<String>,
#[serde(default)]
/// Package directories. Treat the parent directory as a `library`, but only add files from the specified directory.
/// eg: `/usr/local/share/lua/5.1/module`
pub package_dirs: Vec<String>,
#[serde(default)]
/// Workspace roots. eg: ["src", "test"]
pub workspace_roots: Vec<String>,
// unused
Expand Down Expand Up @@ -45,6 +49,7 @@ impl Default for EmmyrcWorkspace {
ignore_dir: Vec::new(),
ignore_globs: Vec::new(),
library: Vec::new(),
package_dirs: Vec::new(),
workspace_roots: Vec::new(),
preload_file_size: 0,
encoding: encoding_default(),
Expand Down
3 changes: 3 additions & 0 deletions crates/emmylua_code_analysis/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ impl Emmyrc {

self.workspace.library = process_and_dedup(self.workspace.library.iter(), workspace_root);

self.workspace.package_dirs =
process_and_dedup(self.workspace.package_dirs.iter(), workspace_root);

self.workspace.ignore_dir =
process_and_dedup(self.workspace.ignore_dir.iter(), workspace_root);

Expand Down
11 changes: 10 additions & 1 deletion crates/emmylua_code_analysis/src/db_index/module/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub use workspace::{Workspace, WorkspaceId};
use super::traits::LuaIndex;
use crate::{Emmyrc, FileId};
use std::{
collections::HashMap,
collections::{HashMap, HashSet},
path::{Path, PathBuf},
sync::Arc,
};
Expand Down Expand Up @@ -365,6 +365,15 @@ impl LuaModuleIndex {
}
}

pub fn next_library_workspace_id(&self) -> u32 {
let used: HashSet<u32> = self.workspaces.iter().map(|w| w.id.id).collect();
let mut candidate = 2;
while used.contains(&candidate) {
candidate += 1;
}
candidate
}

#[allow(unused)]
pub fn remove_workspace_root(&mut self, root: &Path) {
self.workspaces.retain(|r| r.root != root);
Expand Down
12 changes: 3 additions & 9 deletions crates/emmylua_code_analysis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ pub struct EmmyLuaAnalysis {
pub compilation: LuaCompilation,
pub diagnostic: LuaDiagnostic,
pub emmyrc: Arc<Emmyrc>,
lib_workspace_counter: u32,
}

impl EmmyLuaAnalysis {
Expand All @@ -58,7 +57,6 @@ impl EmmyLuaAnalysis {
compilation: LuaCompilation::new(emmyrc.clone()),
diagnostic: LuaDiagnostic::new(),
emmyrc,
lib_workspace_counter: 2,
}
}

Expand Down Expand Up @@ -99,15 +97,11 @@ impl EmmyLuaAnalysis {
}

pub fn add_library_workspace(&mut self, root: PathBuf) {
let module_index = self.compilation.get_db_mut().get_module_index_mut();
let id = WorkspaceId {
id: self.lib_workspace_counter,
id: module_index.next_library_workspace_id(),
};
self.lib_workspace_counter += 1;

self.compilation
.get_db_mut()
.get_module_index_mut()
.add_workspace_root(root, id);
module_index.add_workspace_root(root, id);
}

pub fn update_file_by_uri(&mut self, uri: &Uri, text: Option<String>) -> Option<FileId> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ fn find_intersection_members(
member_set.insert(key.clone());

result.push(LuaMemberInfo {
property_owner_id: None,
property_owner_id: member.property_owner_id.clone(),
key: key.clone(),
typ: typ.clone(),
feature: None,
Expand Down
5 changes: 2 additions & 3 deletions crates/emmylua_doc_cli/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use fern::Dispatch;
use log::LevelFilter;
use std::{
path::{Path, PathBuf},
str::FromStr,
sync::Arc,
};

Expand Down Expand Up @@ -96,7 +95,7 @@ pub fn load_workspace(
emmyrc.pre_process_emmyrc(&config_root);

for lib in &emmyrc.workspace.library {
workspace_folders.push(PathBuf::from_str(lib).unwrap());
workspace_folders.push(PathBuf::from(lib));
}

let mut analysis = EmmyLuaAnalysis::new();
Expand All @@ -106,7 +105,7 @@ pub fn load_workspace(
}

for root in &emmyrc.workspace.workspace_roots {
analysis.add_main_workspace(PathBuf::from_str(root).unwrap());
analysis.add_main_workspace(PathBuf::from(root));
}

analysis.update_config(Arc::new(emmyrc));
Expand Down
53 changes: 44 additions & 9 deletions crates/emmylua_ls/src/context/workspace_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,34 @@ use tokio::sync::{Mutex, RwLock};
use tokio_util::sync::CancellationToken;
use wax::Pattern;

#[derive(Clone, Debug)]
pub enum WorkspaceImport {
All,
SubPaths(Vec<PathBuf>),
}

#[derive(Clone, Debug)]
pub struct WorkspaceFolder {
pub root: PathBuf,
pub import: WorkspaceImport,
}

impl WorkspaceFolder {
pub fn new(root: PathBuf) -> Self {
Self {
root,
import: WorkspaceImport::All,
}
}

pub fn with_sub_paths(root: PathBuf, sub_paths: Vec<PathBuf>) -> Self {
Self {
root,
import: WorkspaceImport::SubPaths(sub_paths),
}
}
}

pub struct WorkspaceManager {
analysis: Arc<RwLock<EmmyLuaAnalysis>>,
client: Arc<ClientProxy>,
Expand All @@ -22,7 +50,7 @@ pub struct WorkspaceManager {
file_diagnostic: Arc<FileDiagnostic>,
lsp_features: Arc<LspFeatures>,
pub client_config: ClientConfig,
pub workspace_folders: Vec<PathBuf>,
pub workspace_folders: Vec<WorkspaceFolder>,
pub watcher: Option<notify::RecommendedWatcher>,
pub current_open_files: HashSet<Uri>,
pub match_file_pattern: WorkspaceFileMatcher,
Expand Down Expand Up @@ -128,7 +156,7 @@ impl WorkspaceManager {
}

pub fn add_reload_workspace_task(&self) -> Option<()> {
let config_root: Option<PathBuf> = self.workspace_folders.first().map(PathBuf::from);
let config_root: Option<PathBuf> = self.workspace_folders.first().map(|wf| wf.root.clone());

let emmyrc = load_emmy_config(config_root, self.client_config.clone());
let analysis = self.analysis.clone();
Expand Down Expand Up @@ -235,19 +263,26 @@ impl WorkspaceManager {
return true;
};

let mut file_matched = true;
for workspace in &self.workspace_folders {
if let Ok(relative) = file_path.strip_prefix(workspace) {
file_matched = self.match_file_pattern.is_match(&file_path, relative);
if let Ok(relative) = file_path.strip_prefix(&workspace.root) {
let inside_import = match &workspace.import {
WorkspaceImport::All => true,
WorkspaceImport::SubPaths(paths) => {
paths.iter().any(|p| relative.starts_with(p))
}
};

if file_matched {
// If the file matches the include pattern, we can stop checking further.
break;
if !inside_import {
continue;
}

if self.match_file_pattern.is_match(&file_path, relative) {
return true;
}
}
}

file_matched
false
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub fn make_auto_doc_tag_command(title: &str, tag_name: &str) -> Command {
async fn add_doc_tag(workspace_manager: &RwLock<WorkspaceManager>, tag_name: String) -> Option<()> {
let workspace_manager = workspace_manager.read().await;
let main_workspace = workspace_manager.workspace_folders.first()?;
let emmyrc_path = main_workspace.join(".emmyrc.json");
let emmyrc_path = main_workspace.root.join(".emmyrc.json");
let mut emmyrc = load_configs_raw(vec![emmyrc_path.clone()], None);
drop(workspace_manager);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ async fn add_disable_project(
) -> Option<()> {
let workspace_manager = workspace_manager.read().await;
let main_workspace = workspace_manager.workspace_folders.first()?;
let emmyrc_path = main_workspace.join(".emmyrc.json");
let emmyrc_path = main_workspace.root.join(".emmyrc.json");
let mut emmyrc = load_configs_raw(vec![emmyrc_path.clone()], None);
drop(workspace_manager);

Expand Down
25 changes: 19 additions & 6 deletions crates/emmylua_ls/src/handlers/hover/find_origin.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::collections::HashSet;

use emmylua_code_analysis::{
LuaCompilation, LuaDeclId, LuaMemberId, LuaSemanticDeclId, LuaType, SemanticDeclLevel,
SemanticModel,
LuaCompilation, LuaDeclId, LuaMemberId, LuaSemanticDeclId, LuaType, LuaUnionType,
SemanticDeclLevel, SemanticModel,
};
use emmylua_parser::{LuaAssignStat, LuaAstNode, LuaSyntaxKind, LuaTableExpr, LuaTableField};

Expand Down Expand Up @@ -240,6 +240,21 @@ fn resolve_member_owner(
}
}

// 判断`table`是否为类
fn table_is_class(table_type: &LuaType, depth: usize) -> bool {
if depth > 10 {
return false;
}
match table_type {
LuaType::Ref(_) | LuaType::Def(_) | LuaType::Generic(_) => true,
LuaType::Union(union) => match union.as_ref() {
LuaUnionType::Nullable(t) => table_is_class(t, depth + 1),
LuaUnionType::Multi(ts) => ts.iter().any(|t| table_is_class(t, depth + 1)),
},
_ => false,
}
}

fn resolve_table_field_through_type_inference(
semantic_model: &SemanticModel,
table_field: &LuaTableField,
Expand All @@ -248,10 +263,8 @@ fn resolve_table_field_through_type_inference(
let table_expr = LuaTableExpr::cast(parent)?;
let table_type = semantic_model.infer_table_should_be(table_expr)?;

if !matches!(
table_type,
LuaType::Ref(_) | LuaType::Def(_) | LuaType::Generic(_)
) {
// 必须为类我们才搜索其成员
if !table_is_class(&table_type, 0) {
return None;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub async fn get_client_config_default(
.clone();
let main_workspace_folder = workspace_folders.first();
let client = context.client();
let scope_uri = main_workspace_folder.map(|p| file_path_to_uri(p).unwrap());
let scope_uri = main_workspace_folder.and_then(|p| file_path_to_uri(&p.root));

let mut configs = Vec::new();
let mut used_scope = None;
Expand Down
36 changes: 24 additions & 12 deletions crates/emmylua_ls/src/handlers/initialized/codestyle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,20 @@ use emmylua_code_analysis::update_code_style;
use std::path::PathBuf;
use walkdir::{DirEntry, WalkDir};

use crate::context::{WorkspaceFolder, WorkspaceImport};

const VCS_DIRS: [&str; 3] = [".git", ".hg", ".svn"];

pub fn load_editorconfig(workspace_folders: Vec<PathBuf>) -> Option<()> {
pub fn load_editorconfig(workspace_folders: Vec<WorkspaceFolder>) -> Option<()> {
let mut editorconfig_files = Vec::new();

for workspace in workspace_folders {
// 构建 WalkDir 迭代器,递归遍历工作区目录
let walker = WalkDir::new(&workspace)
.into_iter()
.filter_entry(|e| !is_vcs_dir(e, &VCS_DIRS))
.filter_map(|e| e.ok())
.filter(|e| e.file_type().is_file());
for entry in walker {
let path = entry.path();
if path.ends_with(".editorconfig") {
editorconfig_files.push(path.to_path_buf());
match &workspace.import {
WorkspaceImport::All => collect_editorconfigs(&workspace.root, &mut editorconfig_files),
WorkspaceImport::SubPaths(subs) => {
for sub in subs {
collect_editorconfigs(&workspace.root.join(sub), &mut editorconfig_files);
}
}
}
}
Expand All @@ -43,7 +41,21 @@ pub fn load_editorconfig(workspace_folders: Vec<PathBuf>) -> Option<()> {
Some(())
}

/// 判断目录条目是否应该被包含在遍历中(不被过滤)
fn collect_editorconfigs(root: &PathBuf, results: &mut Vec<PathBuf>) {
let walker = WalkDir::new(root)
.into_iter()
.filter_entry(|e| !is_vcs_dir(e, &VCS_DIRS))
.filter_map(|e| e.ok())
.filter(|e| e.file_type().is_file());
for entry in walker {
let path = entry.path();
if path.ends_with(".editorconfig") {
results.push(path.to_path_buf());
}
}
}

/// 判断目录/文件是否应被包含在遍历中(不被过滤)
fn is_vcs_dir(entry: &DirEntry, vcs_dirs: &[&str]) -> bool {
if entry.file_type().is_dir() {
let name = entry.file_name().to_string_lossy();
Expand Down
Loading