-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Add Source Navigation Support Across XAML, Previewer, and DevTools #20082
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
base: master
Are you sure you want to change the base?
Conversation
|
You can test this PR using the following package version. |
|
@kim Henrik, Please read the following Contributor License Agreement (CLA). If you agree with the CLA, please reply with the following: Contributor License AgreementContribution License AgreementThis Contribution License Agreement ( “Agreement” ) is agreed to by the party signing below ( “You” ), 1. Definitions. “Code” means the computer software code, whether in human-readable or machine-executable form, “Project” means any of the projects owned or managed by AvaloniaUI OÜ and offered under a license “Submit” is the act of uploading, submitting, transmitting, or distributing code or other content to any “Submission” means the Code and any other copyrightable material Submitted by You, including any 2. Your Submission. You must agree to the terms of this Agreement before making a Submission to any 3. Originality of Work. You represent that each of Your Submissions is entirely Your 4. Your Employer. References to “employer” in this Agreement include Your employer or anyone else 5. Licenses. a. Copyright License. You grant AvaloniaUI OÜ, and those who receive the Submission directly b. Patent License. You grant AvaloniaUI OÜ, and those who receive the Submission directly or c. Other Rights Reserved. Each party reserves all rights not expressly granted in this Agreement. 6. Representations and Warranties. You represent that You are legally entitled to grant the above 7. Notice to AvaloniaUI OÜ. You agree to notify AvaloniaUI OÜ in writing of any facts or 8. Information about Submissions. You agree that contributions to Projects and information about 9. Governing Law/Jurisdiction. This Agreement is governed by the laws of the Republic of Estonia, and 10. Entire Agreement/Assignment. This Agreement is the entire agreement between the parties, and AvaloniaUI OÜ dedicates this Contribution License Agreement to the public domain according to the Creative Commons CC0 1. Kim Henrik doesn't seem to be a GitHub user. |
|
Hi @Unrealiter - thanks for opening this PR - this is something that I have a POC for myself but I've not had time yet to open a PR. This PR seems to add 4 separate things:
Personally, I'd definitely be interested in item 1 but we'd probably want to discuss whether this repository is the correct place for 2 and 3. I think we'd be able to get 1 merged more speedily if it were separated into a separate PR. Does that sound like a good idea to you? With regards to 1, I'll leave some comments with my thoughts in the PR for now. |
grokys
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just commenting on the XAML source info part for now.
| /// <summary> | ||
| /// Gets the 1-based column number in the source file where the element is defined. | ||
| /// </summary> | ||
| public int Column { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than the position of the element in the source file, ideally we'd be storing the span of the element in the source file. This would allow us to match elements to source in the opposite direction - that is, when the user moves the caret in the XAML file, we can highlight the relevant control in the preview.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the span strictly necessary in this case?
I’d assume that the IDE/plugin issuing a “source → element selection” query already has a parsed XML tree available (it needs that anyway for things like code completion). Given an arbitrary caret position in the file, it should be able to resolve the corresponding (or nearest) XML node start.
If the Avalonia designer exposed an API like “get all AvaloniaObjects associated with XML node starting at position X”, that should be enough to support the reverse mapping as well, without having to store explicit spans for each element.
| /// This struct is typically used to store the line, column, and file path of a control or object | ||
| /// to enable navigation or diagnostics (for example, jumping to a definition in Visual Studio). | ||
| /// </remarks> | ||
| public readonly struct SourceInfo : IEquatable<SourceInfo> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not a good idea to name a class the same as its containing namepace
| /// This struct is typically used to store the line, column, and file path of a control or object | ||
| /// to enable navigation or diagnostics (for example, jumping to a definition in Visual Studio). | ||
| /// </remarks> | ||
| public readonly struct SourceInfo : IEquatable<SourceInfo> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is more me thinking aloud than anything else, but I'm not sure it's a good idea for this to be a struct? It limits what we can do with this type, and it's going to be boxed anyway if stored in an attached property.
| /// </item> | ||
| /// </list> | ||
| /// </remarks> | ||
| public static class Source |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Source is quite a generic name. I'd probably prefer something more descriptive, such as MAUI's VisualDiagnostics or we might even want to consider adding it directly to the existing Design class?
| /// Defines the attached <see cref="SourceInfo"/> property that stores the source location | ||
| /// information for a control. | ||
| /// </summary> | ||
| public static readonly AttachedProperty<SourceInfo> SourceInfoProperty = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My POC also used an attached property, but I know that MAUI uses a ConditionalWeakTable so that non-BindableObject /AvaloniaObjects can also have their source tracked (I assume). I'm not sure what the use-case for this is however.
@drasticactions @jsuarezruiz I'd be interested in your thoughts here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My main use case was mapping visual elements in the designer to the appropriate XAML location, which is why I chose AvaloniaObject. I think even most of the “non-visual” elements that one might consider for source mapping, like ControlThemes or Styles, inherit from AvaloniaObject.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By keeping the mapping in a separate store instead of an attached property, I could imagine also supporting the reverse operation (e.g., given a span in XAML, find the corresponding AvaloniaObject instances that represent that piece of source code).
| /// can map a runtime type back to its originating XAML file. | ||
| /// </remarks> | ||
| [AttributeUsage(AttributeTargets.Class, Inherited = false)] | ||
| public sealed class XamlSourceInfoAttribute : Attribute |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume this is needed because:
// Add [XamlSourceInfoAttribute] to the generated class.
// Used at design time to locate the original .axaml file,
// since the runtime loader substitutes a fake file name.
This feels like a bit of a hack. Is it not possible to make the runtime loader use the correct filename?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don’t know why there is no filename.
The protocol for the designer even has a XamlFileProjectPath:
public class UpdateXamlMessage
{
public string Xaml { get; set; }
public string AssemblyPath { get; set; }
public string XamlFileProjectPath { get; set; }
}But for me it was null in both Visual Studio and Rider.
In addition, DesignerWindowLoader.LoadDesignerWindow has:
if (xamlFileProjectPath == null)
xamlFileProjectPath = "/Fake.xaml";
// Fabricate fake Uri
baseUri =
new Uri($"avares://{Path.GetFileNameWithoutExtension(assemblyPath)}{xamlFileProjectPath}");|
There is a problem with responsibility which process should "jump to source", from how designer changes are implemented in this PR. But this approach will conflict with kinds of deeper integration between previewer and IDE.
For the old previewer, I think, it would make the most sense to extend Previewer protocol API by adding new message ("JumpToSourceRequested"?) and handle this message on the IDE side. You can find existing previewer API here. |
|
With old DevTools in this repository, we do not add any new features anymore, but once XAML compiler/source related changes are merged in this PR, I can update new DevTools accordingly. Such feature will be part of free community edition. |
|
I agree that, for the designer, adding a JumpToSource action is probably the cleanest solution, though it requires changes in the VS Code, Rider, and Visual Studio (closed source) plugin implementations. This would also probably lead to different implementations in the dev tools (for example, a CLI command in DevTools and an IDE-specific plugin command in the designer), since DevTools is not connected through the designer API, correct? |

What does the pull request do?
This pull request implements AXAML Source Information support for debug builds and adds a corresponding “Jump to Source” experience in Avalonia’s design-time and diagnostic tooling.
Concretely, it:
Related feature request: Add AXAML Source Information and “Jump to Source” support for Debug Builds #19962.
What is the current behavior?
Currently, when working with Avalonia in design-time or diagnostics tooling:
.axamldefinition.What is the updated/expected behavior with this PR?
With this PR:
Debug builds of AXAML views include source metadata (file path, line, and column) for each generated element.
This is added through an attached property:
Avalonia.Markup.Xaml.SourceInfo.Source.SourceInfo.In addition, the XAML compiler will add a
XamlSourceInfoAttributeto each XAML-backed class, mapping it to the path of the corresponding XAML file.Source info generation can be controlled via the
AvaloniaXamlCreateSourceInfoproject setting, which allows you to explicitly enable or disable SourceInfo creation. If this setting is not specified, it defaults totruefor Debug builds andfalsefor non-Debug builds.The Designer allows users to:
.axamlfile at the correct line/column in the configured editor/IDE.The Avalonia.Diagnostics DevTools:
Supported backends: Visual Studio, Rider, Visual Studio Code
IAvaloniaSourceNavigatorinterface and a registry so anyone can plug in their own navigator:For example, to open the XAML file in Windows Notepad, one may register the following
NotepadNavigator:Example in Designer:
2025-11-01.00-03-01.mp4
Example in DevTools:
2025-11-01.00-11-21.mp4
Example in Rider + MouseWheel selection at the end:
2025-11-17.17-22-43.mp4
Breaking changes
None.
Obsoletions / Deprecations
None.
Fixed issues
Fixes #19962