Skip to content

Commit 753e4f3

Browse files
committed
support formatting by adapter EmmyLuaCodeStyle
1 parent 81a2417 commit 753e4f3

File tree

15 files changed

+389
-65
lines changed

15 files changed

+389
-65
lines changed

EmmyLua.LanguageServer/EmmyLua.LanguageServer.csproj

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,15 @@
2121
</PropertyGroup>
2222

2323
<ItemGroup>
24-
<ProjectReference Include="..\EmmyLua\EmmyLua.csproj" />
24+
<ProjectReference Include="..\EmmyLua\EmmyLua.csproj"/>
2525
</ItemGroup>
2626

2727
<ItemGroup>
28-
<PackageReference Include="EmmyLua.LanguageServer.Framework" Version="0.5.1" />
28+
<PackageReference Include="EmmyLua.LanguageServer.Framework" Version="0.6.0" />
29+
</ItemGroup>
30+
31+
<ItemGroup>
32+
<Content Include="Formatting\Dll\**" CopyToOutputDirectory="PreserveNewest"/>
2933
</ItemGroup>
3034

3135
</Project>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
using System.Runtime.InteropServices;
2+
3+
namespace EmmyLua.LanguageServer.Formatting;
4+
5+
public class FormattingBuilder
6+
{
7+
private bool CanUseNative { get; set; } = true;
8+
9+
public FormattingBuilder()
10+
{
11+
FormattingNativeApi.InitNativeApi();
12+
}
13+
14+
public string Format(string code, string filePath)
15+
{
16+
if (!CanUseNative)
17+
{
18+
return string.Empty;
19+
}
20+
21+
var ptr = IntPtr.Zero;
22+
try
23+
{
24+
ptr = FormattingNativeApi.ReformatLuaCode(code, filePath);
25+
if (ptr == IntPtr.Zero)
26+
{
27+
return string.Empty;
28+
}
29+
30+
var result = Marshal.PtrToStringAnsi(ptr);
31+
32+
if (string.IsNullOrEmpty(result))
33+
{
34+
return code;
35+
}
36+
37+
return result;
38+
}
39+
catch (Exception e)
40+
{
41+
Console.Error.WriteLine(e.Message);
42+
CanUseNative = false;
43+
return string.Empty;
44+
}
45+
finally
46+
{
47+
if (ptr != IntPtr.Zero)
48+
{
49+
FormattingNativeApi.FreeReformatResult(ptr);
50+
}
51+
}
52+
}
53+
54+
public string RangeFormat(string code, string filePath, ref int startLine, ref int startChar, ref int endLine,
55+
ref int endChar)
56+
{
57+
if (!CanUseNative)
58+
{
59+
return string.Empty;
60+
}
61+
62+
var ptr = IntPtr.Zero;
63+
try
64+
{
65+
var rangeFormatResult =
66+
FormattingNativeApi.RangeFormatLuaCode(code, filePath, startLine, startChar, endLine, endChar);
67+
ptr = rangeFormatResult.Code;
68+
if (ptr == IntPtr.Zero)
69+
{
70+
return string.Empty;
71+
}
72+
73+
var result = Marshal.PtrToStringAnsi(ptr);
74+
startLine = rangeFormatResult.StartLine;
75+
startChar = rangeFormatResult.StartChar;
76+
endLine = rangeFormatResult.EndLine;
77+
endChar = rangeFormatResult.EndChar;
78+
79+
if (string.IsNullOrEmpty(result))
80+
{
81+
return string.Empty;
82+
}
83+
84+
return result;
85+
}
86+
catch (Exception e)
87+
{
88+
Console.Error.WriteLine(e.Message);
89+
CanUseNative = false;
90+
return string.Empty;
91+
}
92+
finally
93+
{
94+
if (ptr != IntPtr.Zero)
95+
{
96+
FormattingNativeApi.FreeReformatResult(ptr);
97+
}
98+
}
99+
}
100+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
using EmmyLua.LanguageServer.Framework.Protocol.Capabilities.Client.ClientCapabilities;
2+
using EmmyLua.LanguageServer.Framework.Protocol.Capabilities.Server;
3+
using EmmyLua.LanguageServer.Framework.Protocol.Message.DocumentFormatting;
4+
using EmmyLua.LanguageServer.Framework.Protocol.Model;
5+
using EmmyLua.LanguageServer.Framework.Protocol.Model.TextEdit;
6+
using EmmyLua.LanguageServer.Framework.Server.Handler;
7+
using EmmyLua.LanguageServer.Server;
8+
using EmmyLua.LanguageServer.Util;
9+
10+
namespace EmmyLua.LanguageServer.Formatting;
11+
12+
public class FormattingHandler(ServerContext context) : DocumentFormattingHandlerBase
13+
{
14+
private FormattingBuilder Builder { get; } = new();
15+
16+
protected override Task<DocumentFormattingResponse?> Handle(DocumentFormattingParams request,
17+
CancellationToken token)
18+
{
19+
var uri = request.TextDocument.Uri.UnescapeUri;
20+
DocumentFormattingResponse? response = null;
21+
context.ReadyRead(() =>
22+
{
23+
var semanticModel = context.GetSemanticModel(uri);
24+
if (semanticModel is not null)
25+
{
26+
var filePath = semanticModel.Document.Path;
27+
var formattedCode = Builder.Format(semanticModel.Document.Text, filePath);
28+
if (formattedCode.Length > 0)
29+
{
30+
response = new DocumentFormattingResponse(new TextEdit()
31+
{
32+
Range = new DocumentRange(
33+
new Position(0, 0),
34+
new Position(semanticModel.Document.TotalLine + 1, 0)),
35+
NewText = formattedCode
36+
});
37+
}
38+
}
39+
});
40+
41+
return Task.FromResult(response);
42+
}
43+
44+
protected override Task<DocumentFormattingResponse?> Handle(DocumentRangeFormattingParams request,
45+
CancellationToken token)
46+
{
47+
var uri = request.TextDocument.Uri.UnescapeUri;
48+
DocumentFormattingResponse? response = null;
49+
context.ReadyRead(() =>
50+
{
51+
var semanticModel = context.GetSemanticModel(uri);
52+
if (semanticModel is not null)
53+
{
54+
var range = request.Range;
55+
var startLine = range.Start.Line;
56+
var startChar = 0;
57+
var endLine = range.End.Line;
58+
var endChar = 0;
59+
var path = semanticModel.Document.Path;
60+
var formattedCode = Builder.RangeFormat(semanticModel.Document.Text, path,
61+
ref startLine, ref startChar,
62+
ref endLine, ref endChar);
63+
64+
if (formattedCode.Length > 0)
65+
{
66+
response = new DocumentFormattingResponse(new TextEdit()
67+
{
68+
Range = new DocumentRange(
69+
new Position(startLine, startChar),
70+
new Position(endLine + 1, 0)),
71+
NewText = formattedCode
72+
});
73+
}
74+
}
75+
});
76+
77+
return Task.FromResult(response);
78+
}
79+
80+
protected override Task<DocumentFormattingResponse?> Handle(DocumentRangesFormattingParams request,
81+
CancellationToken token)
82+
{
83+
throw new NotImplementedException();
84+
}
85+
86+
protected override Task<DocumentFormattingResponse?> Handle(DocumentOnTypeFormattingParams request,
87+
CancellationToken token)
88+
{
89+
throw new NotImplementedException();
90+
}
91+
92+
public override void RegisterCapability(ServerCapabilities serverCapabilities,
93+
ClientCapabilities clientCapabilities)
94+
{
95+
serverCapabilities.DocumentFormattingProvider = true;
96+
serverCapabilities.DocumentRangeFormattingProvider = true;
97+
}
98+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using System.Reflection;
2+
using System.Runtime.InteropServices;
3+
4+
namespace EmmyLua.LanguageServer.Formatting;
5+
6+
public static class FormattingNativeApi
7+
{
8+
[DllImport("code_format_csharp", CallingConvention = CallingConvention.Cdecl,
9+
CharSet = CharSet.Ansi)]
10+
public static extern IntPtr ReformatLuaCode(string code, string uri);
11+
12+
[StructLayout(LayoutKind.Sequential)]
13+
public struct RangeFormatResult
14+
{
15+
public int StartLine;
16+
public int StartChar;
17+
public int EndLine;
18+
public int EndChar;
19+
public IntPtr Code;
20+
}
21+
22+
[DllImport("code_format_csharp", CallingConvention = CallingConvention.Cdecl,
23+
CharSet = CharSet.Ansi)]
24+
public static extern RangeFormatResult RangeFormatLuaCode(string code, string uri, int startLine, int startChar,
25+
int endLine,
26+
int endChar);
27+
28+
[DllImport("code_format_csharp", CallingConvention = CallingConvention.Cdecl)]
29+
public static extern void FreeReformatResult(IntPtr ptr);
30+
31+
[DllImport("code_format_csharp", CallingConvention = CallingConvention.Cdecl)]
32+
public static extern void UpdateCodeStyle(string workspace, string configPath);
33+
34+
[DllImport("code_format_csharp", CallingConvention = CallingConvention.Cdecl)]
35+
public static extern void RemoveCodeStyle(string workspace);
36+
37+
public static void InitNativeApi()
38+
{
39+
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), DllImportResolver);
40+
}
41+
42+
private static IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
43+
{
44+
if (libraryName == "code_format_csharp")
45+
{
46+
var dllPath = GetDllPath();
47+
if (NativeLibrary.TryLoad(dllPath, out var handle))
48+
{
49+
return handle;
50+
}
51+
52+
Console.Error.WriteLine("Failed to load native library: " + dllPath);
53+
}
54+
55+
// Otherwise, fallback to default import resolver.
56+
return IntPtr.Zero;
57+
}
58+
59+
private static string GetDllPath()
60+
{
61+
var basePath = "Formatting/Dll";
62+
var osFolder = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "Win" :
63+
RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "Linux" :
64+
RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "Mac" : string.Empty;
65+
66+
var archFolder = RuntimeInformation.OSArchitecture == Architecture.X64 ? "x64" :
67+
RuntimeInformation.OSArchitecture == Architecture.Arm64 ? "arm64" : string.Empty;
68+
69+
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, basePath, osFolder, archFolder,
70+
"code_format_csharp");
71+
}
72+
}

EmmyLua.LanguageServer/LanguageServer.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using EmmyLua.LanguageServer.DocumentRender;
1010
using EmmyLua.LanguageServer.DocumentSymbol;
1111
using EmmyLua.LanguageServer.ExecuteCommand;
12+
using EmmyLua.LanguageServer.Formatting;
1213
using EmmyLua.LanguageServer.Framework.Protocol.Message.Initialize;
1314
using EmmyLua.LanguageServer.Framework.Server;
1415
using EmmyLua.LanguageServer.Hover;
@@ -87,5 +88,6 @@
8788
ls.AddHandler(new SemanticTokenHandler(serverContext));
8889
ls.AddHandler(new TypeHierarchyHandler(serverContext));
8990
ls.AddHandler(new ImplementationHandler(serverContext));
91+
ls.AddHandler(new FormattingHandler(serverContext));
9092

9193
await ls.Run();

0 commit comments

Comments
 (0)