From 7c0ecd5f99b1804f55ca5511f01cf9db05f01e17 Mon Sep 17 00:00:00 2001 From: CppCXY <812125110@qq.com> Date: Thu, 18 Apr 2024 17:26:00 +0800 Subject: [PATCH] support library read, support multi workspace root --- .../ResolveAnalyzer/ResolveAnalyzer.cs | 23 ++++++ .../ResolveAnalyzer/ResolveDependencyGraph.cs | 14 ++++ .../CodeAnalysis/Workspace/LuaWorkspace.cs | 77 ++++++++++++++++--- .../Workspace/Module/ModuleGraph.cs | 23 ++++-- EmmyLua/Configuration/ConfigSchema.cs | 3 + EmmyLua/Configuration/SettingManager.cs | 2 + EmmyLua/Resources/std/global.lua | 7 +- EmmyLua/Resources/std/table.lua | 7 +- LanguageServer/Server/ServerContext.cs | 16 ++-- README.md | 4 +- README._CN.md => README_CN.md | 2 + 11 files changed, 148 insertions(+), 30 deletions(-) rename README._CN.md => README_CN.md (98%) diff --git a/EmmyLua/CodeAnalysis/Compilation/Analyzer/ResolveAnalyzer/ResolveAnalyzer.cs b/EmmyLua/CodeAnalysis/Compilation/Analyzer/ResolveAnalyzer/ResolveAnalyzer.cs index 5abfe974..21017b07 100644 --- a/EmmyLua/CodeAnalysis/Compilation/Analyzer/ResolveAnalyzer/ResolveAnalyzer.cs +++ b/EmmyLua/CodeAnalysis/Compilation/Analyzer/ResolveAnalyzer/ResolveAnalyzer.cs @@ -44,6 +44,20 @@ public override void Analyze(AnalyzeContext analyzeContext) } } while (resolveDependencyGraph.CalcDependency()); + foreach (var unResolve in resolveDependencyGraph.UnResolvedList) + { + var unResolved = unResolve.UnResolved; + var state = unResolve.State; + switch (state) + { + case ResolveState.UnResolvedType: + { + FinalResolveType(unResolved); + break; + } + } + } + Context.ClearCache(); } @@ -152,6 +166,15 @@ private void ResolveType(UnResolved unResolved) } } + private void FinalResolveType(UnResolved unResolved) + { + if (unResolved is UnResolvedDeclaration unResolvedDeclaration) + { + var declaration = unResolvedDeclaration.LuaDeclaration; + declaration.DeclarationType = new LuaNamedType(declaration.Ptr.Stringify); + } + } + private void ResolveIndex(UnResolved unResolved) { if (unResolved is UnResolvedDeclaration unResolvedDeclaration) diff --git a/EmmyLua/CodeAnalysis/Compilation/Analyzer/ResolveAnalyzer/ResolveDependencyGraph.cs b/EmmyLua/CodeAnalysis/Compilation/Analyzer/ResolveAnalyzer/ResolveDependencyGraph.cs index 5150fa3e..e95dcaa9 100644 --- a/EmmyLua/CodeAnalysis/Compilation/Analyzer/ResolveAnalyzer/ResolveDependencyGraph.cs +++ b/EmmyLua/CodeAnalysis/Compilation/Analyzer/ResolveAnalyzer/ResolveDependencyGraph.cs @@ -28,6 +28,20 @@ public IEnumerable CanResolvedList } } + public IEnumerable UnResolvedList + { + get + { + foreach (var (unResolved, dict) in Dependencies) + { + foreach (var (state, _) in dict) + { + yield return new CanResolved(unResolved, state); + } + } + } + } + public bool CalcDependency() { var changed = false; diff --git a/EmmyLua/CodeAnalysis/Workspace/LuaWorkspace.cs b/EmmyLua/CodeAnalysis/Workspace/LuaWorkspace.cs index 0f355abe..852961a8 100644 --- a/EmmyLua/CodeAnalysis/Workspace/LuaWorkspace.cs +++ b/EmmyLua/CodeAnalysis/Workspace/LuaWorkspace.cs @@ -6,6 +6,8 @@ namespace EmmyLua.CodeAnalysis.Workspace; public class LuaWorkspace { + private string MainWorkspace { get; set; } = string.Empty; + public LuaFeatures Features { get; set; } private Dictionary Documents { get; set; } = new(); @@ -36,7 +38,7 @@ public static LuaWorkspace Create(string workspacePath, LuaFeatures features) var workspace = new LuaWorkspace(features); if (workspacePath.Length != 0) { - workspace.LoadWorkspace(workspacePath); + workspace.LoadMainWorkspace(workspacePath); } return workspace; @@ -50,20 +52,41 @@ public LuaWorkspace(LuaFeatures features) ModuleGraph.UpdatePattern(features.RequirePattern); if (features.InitStdLib) { - var stdLib = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "std"); - LoadWorkspace(stdLib, true); + InitStdLib(); } } - public void LoadWorkspace(string workspace, bool notFilter = false) + private void InitStdLib() { + var stdLib = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "std"); + LoadWorkspace(stdLib); + } + + private IEnumerable CollectFiles(string directory) + { + var excludeFolders = Features.ExcludeFolders.Select(it => Path.Combine(directory, it.Trim('\\', '/'))).ToList(); + return Features.Extensions.SelectMany(it => + Directory.GetFiles(directory, it, SearchOption.AllDirectories)) + .Where(it => !excludeFolders.Any(it.StartsWith)); + } + + /// this will load all third libraries and workspace files + public void LoadMainWorkspace(string workspace) + { + MainWorkspace = workspace; Monitor?.OnStartLoadWorkspace(); - var excludeFolders = Features.ExcludeFolders.Select(it => Path.Combine(workspace, it.Trim('\\', '/'))).ToList(); - var files = - Features.Extensions.SelectMany(it => Directory.GetFiles(workspace, it, SearchOption.AllDirectories)); - if (!notFilter) + var thirdPartyRoots = Features.ThirdPartyRoots.Select(PreProcessPath); + var files = new List(); + foreach (var thirdPartyRoot in thirdPartyRoots) + { + files.AddRange(CollectFiles(thirdPartyRoot)); + ModuleGraph.AddPackageRoot(thirdPartyRoot); + } + files.AddRange(CollectFiles(workspace)); + ModuleGraph.AddPackageRoot(workspace); + foreach (var workspaceRoot in Features.WorkspaceRoots.Select(PreProcessPath)) { - files = files.Where(it => !excludeFolders.Any(it.StartsWith)); + ModuleGraph.AddPackageRoot(workspaceRoot); } var documents = @@ -86,12 +109,46 @@ public void LoadWorkspace(string workspace, bool notFilter = false) PathToDocument[document.Path] = document.Id; } - ModuleGraph.AddDocuments(workspace, documents); + ModuleGraph.AddDocuments(documents); + Compilation.AddSyntaxTrees(documents.Select(it => (it.Id, it.SyntaxTree))); + Monitor?.OnFinishLoadWorkspace(); + } + public void LoadWorkspace(string workspace) + { + workspace = PreProcessPath(workspace); + Monitor?.OnStartLoadWorkspace(); + var files = CollectFiles(workspace).ToList(); + var documents = + files.AsParallel().Select(file => LuaDocument.OpenDocument(file, Features.Language)).ToList(); + ModuleGraph.AddPackageRoot(workspace); + foreach (var document in documents) + { + if (!PathToDocument.TryGetValue(document.Path, out var id)) + { + document.Id = AllocateId(); + Documents.Add(document.Id, document); + } + else + { + document.Id = id; + Documents[document.Id] = document; + } + + UrlToDocument[document.Uri] = document.Id; + PathToDocument[document.Path] = document.Id; + } + + ModuleGraph.AddDocuments(documents); Compilation.AddSyntaxTrees(documents.Select(it => (it.Id, it.SyntaxTree))); Monitor?.OnFinishLoadWorkspace(); } + private string PreProcessPath(string path) + { + return path.Replace("${workspaceFolder}", MainWorkspace); + } + private LuaDocumentId AllocateId() { return new LuaDocumentId(_idCounter++); diff --git a/EmmyLua/CodeAnalysis/Workspace/Module/ModuleGraph.cs b/EmmyLua/CodeAnalysis/Workspace/Module/ModuleGraph.cs index ed2c098a..cecddbd7 100644 --- a/EmmyLua/CodeAnalysis/Workspace/Module/ModuleGraph.cs +++ b/EmmyLua/CodeAnalysis/Workspace/Module/ModuleGraph.cs @@ -25,17 +25,26 @@ public void UpdatePattern(List pattern) } } - public void AddDocuments(string workspace, List documents) + public void AddPackageRoot(string packageRoot) { - workspace = Path.GetFullPath(workspace); - if (!WorkspaceModule.TryGetValue(workspace, out var root)) + packageRoot = Path.GetFullPath(packageRoot); + if (!WorkspaceModule.ContainsKey(packageRoot)) { - root = new ModuleNode(); - WorkspaceModule.Add(workspace, root); + WorkspaceModule.Add(packageRoot, new ModuleNode()); } + } + public void AddDocuments(List documents) + { foreach (var document in documents) { + var workspace = GetWorkspace(document); + if (!WorkspaceModule.TryGetValue(workspace, out var root)) + { + root = new ModuleNode(); + WorkspaceModule.Add(workspace, root); + } + AddDocument(root, workspace, document); } } @@ -87,6 +96,7 @@ public void AddDocument(ModuleNode root, string workspace, LuaDocument document) { documentIds.Add(documentId); } + break; } } @@ -164,10 +174,9 @@ public string GetWorkspace(LuaDocument document) var documentFullPath = Path.GetFullPath(document.Path); foreach (var node in WorkspaceModule) { - if (documentFullPath.StartsWith(node.Key)) + if (node.Key.Length > workspace.Length && documentFullPath.StartsWith(node.Key)) { workspace = node.Key; - break; } } diff --git a/EmmyLua/Configuration/ConfigSchema.cs b/EmmyLua/Configuration/ConfigSchema.cs index 65b86af0..cd26812e 100644 --- a/EmmyLua/Configuration/ConfigSchema.cs +++ b/EmmyLua/Configuration/ConfigSchema.cs @@ -72,6 +72,9 @@ public class Workspace NullValueHandling = NullValueHandling.Ignore)] public List Library { get; set; } = new(); + [JsonProperty("workspaceRoots", Required = Required.Default, NullValueHandling = NullValueHandling.Ignore)] + public List WorkspaceRoots { get; set; } = new(); + [JsonProperty("preloadFileSize", Required = Required.Default, NullValueHandling = NullValueHandling.Ignore)] public int PreloadFileSize { get; set; } = 2048000; } diff --git a/EmmyLua/Configuration/SettingManager.cs b/EmmyLua/Configuration/SettingManager.cs index a5386327..814a7316 100644 --- a/EmmyLua/Configuration/SettingManager.cs +++ b/EmmyLua/Configuration/SettingManager.cs @@ -47,6 +47,7 @@ public void Watch(string workspace) if (Watcher is null) { Watcher = new FileSystemWatcher(); + Watcher.Created += OnChanged; Watcher.Changed += OnChanged; Watcher.Path = Workspace; Watcher.Filter = ConfigName; @@ -99,6 +100,7 @@ public LuaFeatures GetLuaFeatures() features.ExcludeFolders.AddRange(setting.Workspace.IgnoreDir.Where(it => !excludeHash.Contains(it))); features.DontIndexMaxFileSize = setting.Workspace.PreloadFileSize; features.ThirdPartyRoots.AddRange(setting.Workspace.Library); + features.WorkspaceRoots.AddRange(setting.Workspace.WorkspaceRoots); features.Language.LanguageLevel = setting.Runtime.Version; features.DiagnosticConfig.Globals.UnionWith(setting.Diagnostics.Globals); features.DiagnosticConfig.WorkspaceDisabledCodes.UnionWith(setting.Diagnostics.Disable); diff --git a/EmmyLua/Resources/std/global.lua b/EmmyLua/Resources/std/global.lua index 480e5e3c..e191a174 100644 --- a/EmmyLua/Resources/std/global.lua +++ b/EmmyLua/Resources/std/global.lua @@ -319,7 +319,7 @@ function setmetatable(table, metatable) end ---@overload fun(e:string):any ---@param e string ---@param base number ----@return any +---@return number function tonumber(e, base) end --- @@ -355,3 +355,8 @@ _VERSION = "Lua 5.3" ---@param msgh fun():string ---@return any function xpcall(f, msgh, arg1, ...) end + +---@generic T1, T2, T3, T4 +---@param t [T1, T2, T3, T4] +---@return T1, T2, T3, T4 +function unpack(t) end \ No newline at end of file diff --git a/EmmyLua/Resources/std/table.lua b/EmmyLua/Resources/std/table.lua index 10577470..b3526615 100644 --- a/EmmyLua/Resources/std/table.lua +++ b/EmmyLua/Resources/std/table.lua @@ -106,12 +106,11 @@ function table.sort(list, comp) end --- Returns the elements from the given list. This function is equivalent to --- return `list[i]`, `list[i+1]`, `···`, `list[j]` --- By default, i is 1 and j is #list. ----@generic T... ----@param list [...T] +---@generic T1, T2, T3, T4 ---@param i number ---@param j number ----@return ...T ----@overload fun(list:[...T]): ...T +---@param list [T1, T2, T3, T4] +---@return T1, T2, T3, T4 function table.unpack(list, i, j) end return table diff --git a/LanguageServer/Server/ServerContext.cs b/LanguageServer/Server/ServerContext.cs index 0d1aa9ec..b6e735e0 100644 --- a/LanguageServer/Server/ServerContext.cs +++ b/LanguageServer/Server/ServerContext.cs @@ -3,7 +3,6 @@ using EmmyLua.Configuration; using LanguageServer.Server.Monitor; using LanguageServer.Util; -using Microsoft.Extensions.Logging; using OmniSharp.Extensions.LanguageServer.Protocol.Document; using OmniSharp.Extensions.LanguageServer.Protocol.Models; using OmniSharp.Extensions.LanguageServer.Protocol.Server; @@ -35,7 +34,7 @@ public void StartServer(string workspacePath) SettingManager.Watch(workspacePath); SettingManager.OnSettingChanged += OnConfigChanged; LuaWorkspace.Features = SettingManager.GetLuaFeatures(); - LuaWorkspace.LoadWorkspace(workspacePath); + LuaWorkspace.LoadMainWorkspace(workspacePath); PushWorkspaceDiagnostics(); } finally @@ -92,15 +91,18 @@ private void OnConfigChanged(SettingManager settingManager) private void UpdateFeatures(LuaFeatures newFeatures) { var oldFeatures = LuaWorkspace.Features; - var requirePatternChanged = !newFeatures.RequirePattern.SequenceEqual(oldFeatures.RequirePattern); - var excludeFoldersChanged = !newFeatures.ExcludeFolders.SequenceEqual(oldFeatures.ExcludeFolders); - var extensionsChanged = !newFeatures.Extensions.SequenceEqual(oldFeatures.Extensions); - if (requirePatternChanged || excludeFoldersChanged || extensionsChanged) + var workspaceNeedReload = false; + workspaceNeedReload |= !newFeatures.RequirePattern.SequenceEqual(oldFeatures.RequirePattern); + workspaceNeedReload |= !newFeatures.ExcludeFolders.SequenceEqual(oldFeatures.ExcludeFolders); + workspaceNeedReload |= !newFeatures.Extensions.SequenceEqual(oldFeatures.Extensions); + workspaceNeedReload |= !newFeatures.WorkspaceRoots.SequenceEqual(oldFeatures.WorkspaceRoots); + workspaceNeedReload |= !newFeatures.ThirdPartyRoots.SequenceEqual(oldFeatures.ThirdPartyRoots); + if (workspaceNeedReload) { LuaWorkspace = LuaWorkspace.Create(); LuaWorkspace.Monitor = Monitor; LuaWorkspace.Features = newFeatures; - LuaWorkspace.LoadWorkspace(MainWorkspacePath); + LuaWorkspace.LoadMainWorkspace(MainWorkspacePath); PushWorkspaceDiagnostics(); } else // TODO check condition diff --git a/README.md b/README.md index 9941114c..ea160297 100644 --- a/README.md +++ b/README.md @@ -29,4 +29,6 @@ You can currently use EmmyLuaAnalyzer's language service by checking `Use EmmyLu Currently in beta, everyone is welcome to test and make suggestions. There is still a lot of work in progress and needs to be improved. -## LICENSE \ No newline at end of file +## LICENSE + +[MIT](./LICENSE) diff --git a/README._CN.md b/README_CN.md similarity index 98% rename from README._CN.md rename to README_CN.md index 30929be2..82ab334d 100644 --- a/README._CN.md +++ b/README_CN.md @@ -30,3 +30,5 @@ TODO 当前处于测试版,欢迎大家测试并提出建议, 有很多工作还在进行中,还需要完善 ## LICENSE + +[MIT](./LICENSE)