Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code folding/Indentation marking in DecompilerPanel #1294

Open
Enigmatrix opened this issue Nov 28, 2019 · 3 comments
Open

Code folding/Indentation marking in DecompilerPanel #1294

Enigmatrix opened this issue Nov 28, 2019 · 3 comments
Labels

Comments

@Enigmatrix
Copy link

Enigmatrix commented Nov 28, 2019

Is your feature request related to a problem? Please describe.
For functions that are long and have many nested statements, it is hard to see the scope where a line of code is being executed, since there are so many different scopes exiting and being entered into.

Describe the solution you'd like
It would be nice to have indentation markings, to know which level of scope you are in, just like here:
Indent Marking
Furthermore, if we could 'fold' certain scopes so that the view is less cluttered, it will also be useful.
Code Folding

@astrelsky
Copy link
Contributor

astrelsky commented Nov 28, 2019

I think this is a fantastic idea.

@Wall-AF
Copy link

Wall-AF commented May 21, 2022

Not a perfect solution, but this works:

  1. add a FieldInputListener to the DecompilerPanel to pick up keyboard input (see Keyboard shortcut for moving to other brace - "Decompile:" panel #4264 (comment));
  2. add a property called collapsedToken to class ClangToken together with its getter and setter;
  3. modify the new `DecompilerPanel.keyPressed(...) method to respond to two additional keys (one to collapse a code section and another to reopen it), e.g.:
	public static final KeyStroke SELECT = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
	public static final KeyStroke HIDE = KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0);
	public static final KeyStroke SHOW = KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, 0);

	@Override
	public void keyPressed(KeyEvent ev, BigInteger index, int fieldNum, int row, int col, Field field) {
		KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(ev);
		FieldLocation location = getCursorPosition();
		ClangTextField textField = (ClangTextField) field;
		ClangToken token = textField.getToken(location);

		if (SELECT.equals(keyStroke)) {
			if (DockingUtils.isControlModifier(ev)) {
				tryToGoto(location, field, ev, true);
			}
			else {
				tryToGoto(location, field, ev, false);
			}
		}
		else if (SHOW.equals(keyStroke)) {
			if (token instanceof ClangSyntaxToken) {
				toggleCollapseToken((ClangSyntaxToken) token, false);
			}
		}
		else if (HIDE.equals(keyStroke)) {
			if (token instanceof ClangSyntaxToken) {
				toggleCollapseToken((ClangSyntaxToken) token, true);
			}
		}
	}

	private void toggleCollapseToken(ClangSyntaxToken startToken, boolean isCollapsed) {
		if ("{".equals(startToken.getText())) {
			ClangSyntaxToken closingBrace = DecompilerUtils.getMatchingBrace(startToken);
			if (closingBrace == null) {
				return;
			}

			ClangNode parent = startToken.Parent();
			List<ClangNode> list = new ArrayList<>();
			parent.flatten(list);

			boolean inSection = false;
			for (ClangNode element : list) {
				ClangToken token = (ClangToken) element;
				if (inSection) {
					if ((token instanceof ClangSyntaxToken)) {
						inSection = (closingBrace != token);
					}
					if (inSection) {
						token.setCollapsedToken(isCollapsed);
					}
				}
				else if ((token instanceof ClangSyntaxToken)) {
					inSection |= (startToken == token);
				}
			}

			setDecompileData(decompileData);
		}
	}
  1. modify the private ClangLayoutController.createFieldElementsForLine(...) method to NOT add a token if it has its collapsedToken property set, e.g.:
	private FieldElement[] createFieldElementsForLine(List<ClangToken> tokens) {
		List<FieldElement> elements = new ArrayList<>();
		int columnPosition = 0;
		for (int i = 0; i < tokens.size(); ++i) {
			ClangToken token = tokens.get(i);
			if (token.isCollapsedToken()) {
				continue;
			}

			Color color = syntax_color[token.getSyntaxType()];
			FieldElement el;
			if (token instanceof ClangCommentToken) {
				AttributedString prototype = new AttributedString("prototype", color, metrics);
				Program program = decompilerPanel.getProgram();
				el = CommentUtils.parseTextForAnnotations(token.getText(), program, prototype, 0);
				columnPosition += el/*ements[i]*/.length();
			}
			else {
				AttributedString as = new AttributedString(token.getText(), color, metrics);
				el = new ClangFieldElement(token, as, columnPosition);
				columnPosition += as.length();
			}
			elements.add(el);
		}
		return elements.toArray(FieldElement[]::new);
	}

As this uses the { token as the trigger for collapsing and subsequently reshowing a code block, there is always at least a couple of lines to return.

@Mgamerz
Copy link

Mgamerz commented May 21, 2022

Would love to see this implemented. Right now I have to deal with fun things like:

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants