Skip to content

Commit cd8ebea

Browse files
committed
Extended QueryForm with grid for match captures
* query form no longer clones the tree, otherwise selecting a matched node in the main form would not work since the nodes are not considered equal if they come from a different tree * main form notifies query form about tree deletion which resets GUI * selecting a match capture selects the captured node in the main form * new alias for capture, dynamic capture array and record helper for TTSQueryMatch returning a dynamic capture array
1 parent 03f6a66 commit cd8ebea

File tree

4 files changed

+189
-10
lines changed

4 files changed

+189
-10
lines changed

TreeSitter.Query.pas

+16
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,15 @@ TTSQuery = class
3636
property Query: PTSQuery read FQuery;
3737
end;
3838

39+
TTSQueryCapture = TreeSitterLib.TSQueryCapture;
40+
TTSQueryCaptureArray = array of TTSQueryCapture;
41+
3942
TTSQueryMatch = TreeSitterLib.TSQueryMatch;
4043

44+
TTSQueryMatchHelper = record helper for TTSQueryMatch
45+
function CapturesArray: TTSQueryCaptureArray;
46+
end;
47+
4148
TTSQueryCursor = class
4249
strict private
4350
FQueryCursor: PTSQueryCursor;
@@ -196,4 +203,13 @@ procedure TTSQueryCursor.SetMaxStartDepth(AMaxStartDepth: UInt32);
196203
ts_query_cursor_set_max_start_depth(FQueryCursor, AMaxStartDepth);
197204
end;
198205

206+
{ TTSQueryMatchHelper }
207+
208+
function TTSQueryMatchHelper.CapturesArray: TTSQueryCaptureArray;
209+
begin
210+
SetLength(Result, capture_count);
211+
if capture_count > 0 then
212+
Move(captures[0], Result[0], capture_count * SizeOf(captures[0]));
213+
end;
214+
199215
end.

VCLDemo/frmDTSMain.pas

+5-1
Original file line numberDiff line numberDiff line change
@@ -324,16 +324,20 @@ procedure TDTSMainForm.ParseContent;
324324
begin
325325
treeView.Items.Clear;
326326
sCode:= memCode.Lines.Text;
327+
if DTSQueryForm <> nil then
328+
DTSQueryForm.TreeDeleted;
329+
FreeAndNil(FTree);
327330
if Length(sCode) = 0 then
328331
Exit; //avoid our own exception that empty string cannot be parsed
329332
//we no longer pass OldTree as we would need to track editing and call
330333
//ts_tree_edit
331-
FreeAndNil(FTree);
332334
FTree:= FParser.ParseString(sCode);
333335
root:= FTree.RootNode;
334336
rootNode:= TTSTreeViewNode(treeView.Items.AddChild(nil, root.NodeType));
335337
rootNode.SetupTSNode(root);
336338
FEditChanged:= False;
339+
if DTSQueryForm <> nil then
340+
DTSQueryForm.NewTreeGenerated(FTree);
337341
end;
338342

339343
procedure TDTSMainForm.SetSelectedTSNode(const Value: TTSNode);

VCLDemo/frmDTSQuery.dfm

+62-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ object DTSQueryForm: TDTSQueryForm
33
Top = 0
44
Caption = 'Query'
55
ClientHeight = 441
6-
ClientWidth = 624
6+
ClientWidth = 764
77
Color = clBtnFace
88
Font.Charset = DEFAULT_CHARSET
99
Font.Color = clWindowText
@@ -16,7 +16,7 @@ object DTSQueryForm: TDTSQueryForm
1616
object Splitter1: TSplitter
1717
Left = 0
1818
Top = 250
19-
Width = 624
19+
Width = 764
2020
Height = 3
2121
Cursor = crVSplit
2222
Align = alTop
@@ -34,7 +34,7 @@ object DTSQueryForm: TDTSQueryForm
3434
object pnlTop: TPanel
3535
Left = 0
3636
Top = 0
37-
Width = 624
37+
Width = 764
3838
Height = 50
3939
Align = alTop
4040
BevelOuter = bvNone
@@ -58,7 +58,7 @@ object DTSQueryForm: TDTSQueryForm
5858
object memQuery: TMemo
5959
Left = 0
6060
Top = 50
61-
Width = 624
61+
Width = 764
6262
Height = 200
6363
Align = alTop
6464
Font.Charset = DEFAULT_CHARSET
@@ -115,4 +115,62 @@ object DTSQueryForm: TDTSQueryForm
115115
TabOrder = 1
116116
end
117117
end
118+
object pnlMatches: TPanel
119+
Left = 353
120+
Top = 253
121+
Width = 411
122+
Height = 188
123+
Align = alClient
124+
BevelOuter = bvNone
125+
TabOrder = 3
126+
object pnlMatchesTop: TPanel
127+
Left = 0
128+
Top = 0
129+
Width = 411
130+
Height = 41
131+
Align = alTop
132+
BevelOuter = bvNone
133+
TabOrder = 0
134+
object lblMatch: TLabel
135+
Left = 219
136+
Top = 13
137+
Width = 3
138+
Height = 15
139+
end
140+
object btnMatchStart: TButton
141+
Left = 9
142+
Top = 9
143+
Width = 120
144+
Height = 25
145+
Caption = 'Start query cursor'
146+
Enabled = False
147+
TabOrder = 0
148+
OnClick = btnMatchStartClick
149+
end
150+
object btnMatchNext: TButton
151+
Left = 132
152+
Top = 9
153+
Width = 75
154+
Height = 25
155+
Caption = 'Next match'
156+
Enabled = False
157+
TabOrder = 1
158+
OnClick = btnMatchNextClick
159+
end
160+
end
161+
object sgMatchCaptures: TStringGrid
162+
Left = 0
163+
Top = 41
164+
Width = 411
165+
Height = 147
166+
Align = alClient
167+
ColCount = 2
168+
DefaultColWidth = 120
169+
DefaultRowHeight = 18
170+
RowCount = 1
171+
FixedRows = 0
172+
TabOrder = 1
173+
OnSelectCell = sgMatchCapturesSelectCell
174+
end
175+
end
118176
end

VCLDemo/frmDTSQuery.pas

+106-5
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,31 @@ TDTSQueryForm = class(TForm)
2020
cbPatternIdx: TComboBox;
2121
sgPredicateSteps: TStringGrid;
2222
Splitter2: TSplitter;
23+
pnlMatches: TPanel;
24+
pnlMatchesTop: TPanel;
25+
sgMatchCaptures: TStringGrid;
26+
btnMatchStart: TButton;
27+
btnMatchNext: TButton;
28+
lblMatch: TLabel;
2329
procedure btnExecuteClick(Sender: TObject);
2430
procedure FormDestroy(Sender: TObject);
2531
procedure FormClose(Sender: TObject; var Action: TCloseAction);
2632
procedure cbPatternIdxClick(Sender: TObject);
33+
procedure btnMatchStartClick(Sender: TObject);
34+
procedure btnMatchNextClick(Sender: TObject);
35+
procedure sgMatchCapturesSelectCell(Sender: TObject; ACol, ARow: Integer;
36+
var CanSelect: Boolean);
2737
private
2838
FTree: TTSTree;
2939
FQuery: TTSQuery;
3040
FQueryCursor: TTSQueryCursor;
41+
FCurrentMatch: TTSQueryMatch;
42+
procedure ClearQuery;
43+
procedure ClearMatches;
44+
procedure ClearPredicates;
3145
public
32-
{ Public declarations }
46+
procedure TreeDeleted;
47+
procedure NewTreeGenerated(ATree: TTSTree);
3348
end;
3449

3550
var
@@ -39,6 +54,9 @@ procedure ShowQueryForm(ATree: TTSTree);
3954

4055
implementation
4156

57+
uses
58+
Math, frmDTSMain;
59+
4260
{$R *.dfm}
4361

4462
procedure ShowQueryForm(ATree: TTSTree);
@@ -47,7 +65,7 @@ procedure ShowQueryForm(ATree: TTSTree);
4765
begin
4866
Application.Createform(TDTSQueryForm, DTSQueryForm);
4967
end;
50-
DTSQueryForm.FTree:= ATree.Clone;
68+
DTSQueryForm.FTree:= ATree;
5169
DTSQueryForm.cbPatternIdxClick(nil);
5270
DTSQueryForm.Show;
5371
DTSQueryForm.BringToFront;
@@ -64,8 +82,8 @@ procedure TDTSQueryForm.btnExecuteClick(Sender: TObject);
6482
errorType: TTSQueryError;
6583
i: Integer;
6684
begin
67-
cbPatternIdx.Items.Clear;
68-
FreeAndNil(FQuery);
85+
ClearQuery;
86+
6987
FQuery:= TTSQuery.Create(FTree.Language, memQuery.Lines.Text, errorOffset, errorType);
7088
if errorType <> TTSQueryError.TSQueryErrorNone then
7189
begin
@@ -80,12 +98,50 @@ procedure TDTSQueryForm.btnExecuteClick(Sender: TObject);
8098
[FQuery.PatternCount, FQuery.CaptureCount, FQuery.StringCount]);
8199
for i:= 0 to FQuery.PatternCount - 1 do
82100
cbPatternIdx.Items.Add(IntToStr(i));
101+
btnMatchStart.Enabled:= True;
83102
end;
84103
if cbPatternIdx.Items.Count > 0 then
85104
cbPatternIdx.ItemIndex:= 0;
86105
cbPatternIdxClick(nil);
87106
end;
88107

108+
procedure TDTSQueryForm.btnMatchNextClick(Sender: TObject);
109+
var
110+
i: Integer;
111+
captures: TTSQueryCaptureArray;
112+
begin
113+
if not FQueryCursor.NextMatch(FCurrentMatch) then
114+
begin
115+
ClearMatches;
116+
lblMatch.Caption:= 'No more matches';
117+
Exit;
118+
end;
119+
lblMatch.Caption:= Format('Match id = %d, pattern idx = %d', [FCurrentMatch.id, FCurrentMatch.pattern_index]);
120+
121+
captures:= FCurrentMatch.CapturesArray;
122+
sgMatchCaptures.RowCount:= Length(captures) + 1;
123+
sgMatchCaptures.FixedRows:= 1;
124+
for i:= 0 to FCurrentMatch.capture_count - 1 do
125+
begin
126+
sgMatchCaptures.Cells[0, i + 1]:= IntToStr(captures[i].index);
127+
sgMatchCaptures.Cells[1, i + 1]:= captures[i].node.NodeType;
128+
end;
129+
if InRange(sgMatchCaptures.Selection.Top, 1, Length(captures)) then
130+
DTSMainForm.SelectedTSNode:= captures[sgMatchCaptures.Selection.Top - 1].node;
131+
end;
132+
133+
procedure TDTSQueryForm.btnMatchStartClick(Sender: TObject);
134+
begin
135+
if FQueryCursor = nil then
136+
FQueryCursor:= TTSQueryCursor.Create;
137+
FQueryCursor.Execute(FQuery, FTree.RootNode);
138+
ClearMatches;
139+
sgMatchCaptures.Cells[0, 0]:= 'Capture index';
140+
sgMatchCaptures.Cells[1, 0]:= 'Node';
141+
btnMatchNext.Enabled:= True;
142+
btnMatchNextClick(nil);
143+
end;
144+
89145
procedure TDTSQueryForm.cbPatternIdxClick(Sender: TObject);
90146
const
91147
stepTypeStrings: array[TTSQueryPredicateStepType] of string = (
@@ -131,6 +187,28 @@ procedure TDTSQueryForm.cbPatternIdxClick(Sender: TObject);
131187
end;
132188
end;
133189

190+
procedure TDTSQueryForm.ClearMatches;
191+
begin
192+
sgMatchCaptures.RowCount:= 1;
193+
lblMatch.Caption:= '';
194+
btnMatchNext.Enabled:= False;
195+
end;
196+
197+
procedure TDTSQueryForm.ClearPredicates;
198+
begin
199+
cbPatternIdx.Items.Clear;
200+
sgPredicateSteps.RowCount:= 1;
201+
end;
202+
203+
procedure TDTSQueryForm.ClearQuery;
204+
begin
205+
FreeAndNil(FQuery);
206+
btnMatchStart.Enabled:= False;
207+
lblQueryState.Caption:= '';
208+
ClearPredicates;
209+
ClearMatches;
210+
end;
211+
134212
procedure TDTSQueryForm.FormClose(Sender: TObject; var Action: TCloseAction);
135213
begin
136214
Action:= caFree;
@@ -140,9 +218,32 @@ procedure TDTSQueryForm.FormDestroy(Sender: TObject);
140218
begin
141219
FreeAndNil(FQueryCursor);
142220
FreeAndNil(FQuery);
143-
FreeAndNil(FTree);
221+
//FTree is no longer a clone/copy but identical to main form, otherwise
222+
//finding the node in the main forms tree would not work
223+
//(nodes belowing to different trees are not considered equal)
224+
FTree:= nil;
144225
if Self = DTSQueryForm then
145226
DTSQueryForm:= nil;
146227
end;
147228

229+
procedure TDTSQueryForm.NewTreeGenerated(ATree: TTSTree);
230+
begin
231+
ClearQuery;
232+
FTree:= ATree;
233+
end;
234+
235+
procedure TDTSQueryForm.sgMatchCapturesSelectCell(Sender: TObject; ACol,
236+
ARow: Integer; var CanSelect: Boolean);
237+
begin
238+
if not InRange(ARow, 1, FCurrentMatch.capture_count) then
239+
Exit;
240+
241+
DTSMainForm.SelectedTSNode:= FCurrentMatch.captures[ARow - 1].node;
242+
end;
243+
244+
procedure TDTSQueryForm.TreeDeleted;
245+
begin
246+
ClearQuery;
247+
end;
248+
148249
end.

0 commit comments

Comments
 (0)