Merge remote-tracking branch 'origin/GP-6443-dragonmacher-decomp-find-text-infinity--SQUASHED'

This commit is contained in:
Ryan Kurtz
2026-02-17 17:06:59 -05:00
8 changed files with 42 additions and 27 deletions
@@ -175,8 +175,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
FieldElement[] elements = createFieldElementsForLine(tokens); FieldElement[] elements = createFieldElementsForLine(tokens);
int indent = line.getIndent() * indentWidth; int indent = line.getIndent() * indentWidth;
int updatedMaxWidth = maxWidth; return new ClangTextField(tokens, elements, indent, line.getLineNumber(), maxWidth,
return new ClangTextField(tokens, elements, indent, line.getLineNumber(), updatedMaxWidth,
hlFactory); hlFactory);
} }
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -33,7 +33,7 @@ public class ClangTextField extends WrappingVerticalLayoutTextField {
public ClangTextField(List<ClangToken> tokenList, FieldElement[] fieldElements, int x, public ClangTextField(List<ClangToken> tokenList, FieldElement[] fieldElements, int x,
int lineNumber, int width, FieldHighlightFactory hlFactory) { int lineNumber, int width, FieldHighlightFactory hlFactory) {
super(createSingleLineElement(fieldElements), x, width - x, 30, hlFactory, false); super(createSingleLineElement(fieldElements), x, width - x, 30, hlFactory, false, "");
this.tokenList = tokenList; this.tokenList = tokenList;
this.lineNumber = lineNumber; this.lineNumber = lineNumber;
} }
@@ -113,7 +113,7 @@ public class DecompilerSearchResults extends SearchResults {
// getNextLocation() will find the next matching location, starting at the given field // getNextLocation() will find the next matching location, starting at the given field
// location. The next location may or may not actually contain the given field location. // location. The next location may or may not actually contain the given field location.
DecompilerSearchLocation nextLocation = getNextLocation(fieldLocation, searchForward); DecompilerSearchLocation nextLocation = getNextLocation(fieldLocation, searchForward);
if (nextLocation.contains(fieldLocation)) { if (nextLocation != null && nextLocation.contains(fieldLocation)) {
return nextLocation; return nextLocation;
} }
return null; return null;
@@ -119,7 +119,7 @@ public class DecompilerSearcher implements FindDialogSearcher {
FieldLocation last = searchLocation.getFieldLocation(); FieldLocation last = searchLocation.getFieldLocation();
int line = last.getIndex().intValue(); int line = last.getIndex().intValue();
int field = 0; // there is only 1 field int field = 0; // there is only 1 field
int row = 0; // there is only 1 row int row = last.getRow(); // more than 1 row when the line wraps
int col = last.getCol() + 1; // move over one char to handle sub-matches int col = last.getCol() + 1; // move over one char to handle sub-matches
start = new FieldLocation(line, field, row, col); start = new FieldLocation(line, field, row, col);
searchLocation = findNext(forwardMatcher, searchText, start); searchLocation = findNext(forwardMatcher, searchText, start);
@@ -215,7 +215,6 @@ public class DecompilerSearcher implements FindDialogSearcher {
private DecompilerSearchLocation findNext(Function<String, SearchMatch> matcher, private DecompilerSearchLocation findNext(Function<String, SearchMatch> matcher,
String searchString, FieldLocation currentLocation) { String searchString, FieldLocation currentLocation) {
List<Field> fields = decompilerPanel.getFields(); List<Field> fields = decompilerPanel.getFields();
int line = currentLocation.getIndex().intValue(); int line = currentLocation.getIndex().intValue();
for (int i = line; i < fields.size(); i++) { for (int i = line; i < fields.size(); i++) {
@@ -234,6 +233,8 @@ public class DecompilerSearcher implements FindDialogSearcher {
// the match start is relative to the cursor position. Update the start to // the match start is relative to the cursor position. Update the start to
// compensate for the difference between the start of the line and the cursor. // compensate for the difference between the start of the line and the cursor.
// //
// The match start/end for a partial line will be relative to that line's text. We
// want the start/end to be based on a full line.
int cursorOffset = fullLine.length() - partialLine.length(); int cursorOffset = fullLine.length() - partialLine.length();
match.start += cursorOffset; match.start += cursorOffset;
match.end += cursorOffset; match.end += cursorOffset;
@@ -241,7 +242,7 @@ public class DecompilerSearcher implements FindDialogSearcher {
FieldLineLocation lineInfo = getFieldIndexFromOffset(match.start, field); FieldLineLocation lineInfo = getFieldIndexFromOffset(match.start, field);
FieldLocation fieldLocation = FieldLocation fieldLocation =
new FieldLocation(i, lineInfo.fieldNumber(), 0, lineInfo.column()); new FieldLocation(i, lineInfo.fieldNumber(), lineInfo.row(), lineInfo.column());
int lineNumber = lineInfo.lineNumber(); int lineNumber = lineInfo.lineNumber();
SearchLocationContext context = createContext(fullLine, match); SearchLocationContext context = createContext(fullLine, match);
return new DecompilerSearchLocation(fieldLocation, match.start, match.end - 1, return new DecompilerSearchLocation(fieldLocation, match.start, match.end - 1,
@@ -266,31 +267,35 @@ public class DecompilerSearcher implements FindDialogSearcher {
private String substring(ClangTextField textField, FieldLocation location, private String substring(ClangTextField textField, FieldLocation location,
boolean forwardSearch) { boolean forwardSearch) {
// Note: full text may include multiple wrapped UI lines that are then put back into one
// line here for searching.
String fullText = textField.getText();
if (location == null) { // the cursor location is not on this line; use all of the text if (location == null) { // the cursor location is not on this line; use all of the text
return textField.getText(); return fullText;
} }
if (textField.getText().isEmpty()) { // the cursor is on blank line if (fullText.isEmpty()) { // the cursor is on blank line
return ""; return "";
} }
String partialText = textField.getText(); int row = location.getRow();
if (forwardSearch) { int nextCol = location.getCol();
int screenCol = textField.screenLocationToTextOffset(row, nextCol);
int nextCol = location.getCol(); if (forwardSearch) {
// protects against the location column being out of range (this can happen if we're // protects against the location column being out of range (this can happen if we're
// searching forward and the cursor is past the last token) // searching forward and the cursor is past the last token)
if (nextCol >= partialText.length()) { if (screenCol >= fullText.length()) {
return ""; return "";
} }
// skip a character to start the next search; this prevents matching the previous match // skip a character to start the next search; this prevents matching the previous match
return partialText.substring(nextCol); return fullText.substring(screenCol);
} }
// backwards search // backwards search
return partialText.substring(0, location.getCol()); return fullText.substring(0, screenCol);
} }
private FieldLineLocation getFieldIndexFromOffset(int screenOffset, ClangTextField textField) { private FieldLineLocation getFieldIndexFromOffset(int screenOffset, ClangTextField textField) {
@@ -298,8 +303,10 @@ public class DecompilerSearcher implements FindDialogSearcher {
RowColLocation rowColLocation = textField.textOffsetToScreenLocation(screenOffset); RowColLocation rowColLocation = textField.textOffsetToScreenLocation(screenOffset);
int lineNumber = textField.getLineNumber(); int lineNumber = textField.getLineNumber();
// we use 0 here because currently there is only one field, which is the entire line int fieldNumber = 0; // We use 0 here because there is only one field, which is the entire line
return new FieldLineLocation(0, lineNumber, rowColLocation.col()); int row = rowColLocation.row();
int col = rowColLocation.col();
return new FieldLineLocation(fieldNumber, lineNumber, row, col);
} }
private static class SearchMatch { private static class SearchMatch {
@@ -323,5 +330,5 @@ public class DecompilerSearcher implements FindDialogSearcher {
} }
} }
private record FieldLineLocation(int fieldNumber, int lineNumber, int column) {} private record FieldLineLocation(int fieldNumber, int lineNumber, int row, int column) {}
} }
@@ -48,7 +48,12 @@ public class FindReferencesToAddressAction extends AbstractFindReferencesToAddre
if (!(context instanceof DecompilerActionContext dac)) { if (!(context instanceof DecompilerActionContext dac)) {
return false; return false;
} }
updateMenuName(dac.getAddress());
Address address = dac.getAddress();
if (address == null) {
return false;
}
updateMenuName(address);
return super.isEnabledForContext(context); return super.isEnabledForContext(context);
} }
@@ -136,7 +136,7 @@ public class VerticalLayoutTextField implements TextField {
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
String text = elements.get(i).getText(); String text = elements.get(i).getText();
buf.append(text); buf.append(text);
if (!text.endsWith(delimiter)) { // prevent 2 spaces between merged lines if (!text.endsWith(delimiter)) { // prevent 2 delimiters between merged lines
buf.append(delimiter); buf.append(delimiter);
} }
} }
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -47,11 +47,14 @@ public class WrappingVerticalLayoutTextField extends VerticalLayoutTextField {
* @param maxLines is the max number of lines to display * @param maxLines is the max number of lines to display
* @param hlFactory is the highlight factory * @param hlFactory is the highlight factory
* @param breakOnWhiteSpace is true if wrapping should break on word boundaries * @param breakOnWhiteSpace is true if wrapping should break on word boundaries
* @param rowSeparator The string used to space lines of text when concatenated by the
* getText() method.
*/ */
public WrappingVerticalLayoutTextField(FieldElement textElement, int startX, int width, public WrappingVerticalLayoutTextField(FieldElement textElement, int startX, int width,
int maxLines, FieldHighlightFactory hlFactory, boolean breakOnWhiteSpace) { int maxLines, FieldHighlightFactory hlFactory, boolean breakOnWhiteSpace,
String rowSeparator) {
super(FieldUtils.wrap(textElement, width, breakOnWhiteSpace), startX, width, maxLines, super(FieldUtils.wrap(textElement, width, breakOnWhiteSpace), startX, width, maxLines,
hlFactory, " "); hlFactory, rowSeparator);
} }
@Override @Override
@@ -21,6 +21,7 @@ import org.jdom2.Element;
import docking.widgets.fieldpanel.Layout; import docking.widgets.fieldpanel.Layout;
import docking.widgets.fieldpanel.field.Field; import docking.widgets.fieldpanel.field.Field;
import generic.json.Json;
/** /**
* Class to represent {@link Field} locations within the field viewer. * Class to represent {@link Field} locations within the field viewer.
@@ -207,7 +208,7 @@ public class FieldLocation implements Comparable<FieldLocation> {
@Override @Override
public String toString() { public String toString() {
return index.toString() + ", " + fieldNum + ", " + row + ", " + col; return Json.toString(this, "index", "fieldNum", "row", "col");
} }