From e09409e2ed15b28cdeedbe1029c5bcea6a96a76f Mon Sep 17 00:00:00 2001 From: Rio Sanjaya Date: Sun, 26 Jul 2026 16:26:47 +0700 Subject: [PATCH 1/2] Remove fields completely with empty string values LSP spec allows omitting non-mandatory fields, empty string can be problematic, Zed for example doesn't consider empty string as 'falsy' --- src/protocol/LSP.Base.pas | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/protocol/LSP.Base.pas b/src/protocol/LSP.Base.pas index 9a3c01a..9fc9b42 100644 --- a/src/protocol/LSP.Base.pas +++ b/src/protocol/LSP.Base.pas @@ -168,6 +168,7 @@ TLSPLocalDispatcher = class(TLSPBaseDispatcher) Class function GetLogFile: String; static; procedure SetDispatcher(AValue: TLSPBaseDispatcher); Class procedure SetLogFile(const AValue: String); static; + class procedure RemoveEmptyStringKeys(AData: TJSONData); static; Protected Class Procedure DoLog(const Msg : String); Class Procedure DoLog(const Fmt : String; Const Args : Array of const); @@ -556,6 +557,43 @@ class function TLSPContext.HaveLog: Boolean; Result:=assigned(_LogFile); end; + +class procedure TLSPContext.RemoveEmptyStringKeys(AData: TJSONData); +var + i: Integer; + Child: TJSONData; + Obj: TJSONObject; + Arr: TJSONArray; +begin + if AData = nil then Exit; + + case AData.JSONType of + jtObject: + begin + Obj := TJSONObject(AData); + for i := Obj.Count - 1 downto 0 do + begin + Child := Obj.Items[i]; + + if (Child.JSONType = jtString) and (Child.AsString = '') then + begin + if (Obj.Names[I] = 'sortText') or (Obj.Names[I] = 'filterText') or + (Obj.Names[I] = 'insertText') then + Obj.Delete(i); + end else + RemoveEmptyStringKeys(Child); + end; + end; + + jtArray: + begin + Arr := TJSONArray(AData); + for i := 0 to Arr.Count - 1 do + RemoveEmptyStringKeys(Arr.Items[i]); + end; + end; +end; + function TLSPContext.Execute(aRequest: TJSONData): TJSONData; begin @@ -563,6 +601,9 @@ function TLSPContext.Execute(aRequest: TJSONData): TJSONData; DoLog('Executing request: %s',[aRequest.AsJSON]); try Result:=Dispatcher.ExecuteRequest(aRequest); + + TLSPContext.RemoveEmptyStringKeys(Result); + If HaveLog then if Result<>Nil then DoLog('Request response: %s',[Result.AsJSON]) From a8df31134421856a94c5ef1fb5da4bccaea8d60b Mon Sep 17 00:00:00 2001 From: Rio Sanjaya Date: Sun, 26 Jul 2026 16:32:21 +0700 Subject: [PATCH 2/2] Ident Completion: Use textEdit field instead of falling back to label --- src/serverprotocol/PasLS.Completion.pas | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/serverprotocol/PasLS.Completion.pas b/src/serverprotocol/PasLS.Completion.pas index 2cc56a7..036241b 100644 --- a/src/serverprotocol/PasLS.Completion.pas +++ b/src/serverprotocol/PasLS.Completion.pas @@ -129,6 +129,7 @@ function TCompletion.Process(var Params: TCompletionParams): TCompletionList; Identifier: TIdentifierListItem; Completion: TCompletionItem; OverloadMap: TFPHashList; + TextEdit: TTextEdit; IdentContext, IdentDetails: ShortString; ObjectMember: boolean; Kind: TCompletionItemKind; @@ -224,6 +225,11 @@ function TCompletion.Process(var Params: TCompletionParams): TCompletionList; Completion := Completions.Add; Completion.&label := Identifier.Identifier; + Completion.insertTextFormat := TInsertTextFormat.PlainText; + TextEdit := TTextEdit.Create(nil); + TextEdit.newText := Identifier.Identifier; + TextEdit.range.SetRange(Y, PStart - 1, Length(CodeToolBoss.IdentifierList.Prefix)); + Completion.SetTextEdit(TextEdit); if not ServerSettings.minimalisticCompletions then Completion.detail := IdentDetails; Completion.kind := Kind;