abap2UI5 for VS Code
A VS Code extension for abap2UI5abap2UI5One ABAP class implementing z2ui5_if_app is the whole app: the browser loads a generic UI5 shell once, and every interaction after that is an HTTP roundtrip back into ABAP.community org, abap apps: F9 starts the app class in a preview beside the source, Ctrl+F3 activates the class and reloads it, and the abap2UI5 linter runs in the editor while you type.
Developing an abap2UI5abap2UI5One ABAP class implementing z2ui5_if_app is the whole app: the browser loads a generic UI5 shell once, and every interaction after that is an HTTP roundtrip back into ABAP.community org, abap app the plain way means three windows: the editor with the class, a browser with the app, and the activation step between them. This extension puts the app in an editor tab beside the source — F9 launches the class through a local auth proxy that attaches the stored SAP credentials to every request, Ctrl+F3 activates and reloads, and an activation done elsewhere is noticed through the class's ADT metadata on the server. The abap2UI5 linter's property gate runs in process while you type, so a control the targeted UI5 release does not have is a squiggle rather than a blank screen, with quick fixes, waivers the project's CI run honours and a findings view grouped by rule; completion and hover answer for the UI5 API inside the builder chain and for the binding paths the class's own model has. *Preview View (No System)* and *Run Unit Tests (No System)* work with no system at all, and the language half runs in vscode.dev, github.dev and browser-based SAP Business Application StudioSAP Business Application StudioSAP's development environment for business applications, delivered as a cloud service and opened in a browser, where work happens in preconfigured dev spaces.sap official, btp. Beside the editor features it registers the project's MCP server for every MCP client in the window and hosts a second server of its own for the configured systems, the credentials and the proxy.
Facts
Where it sits
Who is behind it
In the atlas: maintains 18@abap2UI5oblomov-devoblomov-devMaintains this
In the atlas: maintains 4, contributes to 44@oblomov-devmaintainers
Before choosing this
Conditions
PracticalFunctional boundary
The embedded preview needs a system that accepts basic auth: the documentation states that pure SSO or SAML without a basic-auth fallback is not supported, and points at external mode, which opens the app in the normal browser with the existing SAP session. On the web only the language half runs; the preview with its proxy, traffic log and screenshot, Ctrl+F3 activation, the render gate, the workspace-wide check and the MCP servers are desktop-only. The agent app tools send nothing until abap2ui5.agent.enableAppTools is switched on in User settings, because an event they fire runs as the configured user and changes data on the system for real.
PolicySAP API Policy 4.2026a§ 1.1, 1.2, 2.2.2from Agent access to your systemsAn MCP server uses SAP APIs to supply tools, resources or data to an agent.
- § 1.1 Published APIs. Published APIs are made available for the documented purposes identified by SAP, including integration, extensions and data exchange.
- § 1.2 Non-Published APIs. Customer and third-party applications must not access non-published APIs except where documentation permits it or SAP otherwise authorizes it.
- § 2.2.2 AI call sequences and data extraction. Outside SAP-endorsed architectures, data services or expressly identified pathways, the policy prohibits specified agentic API use and systematic or large-scale extraction.
MCP does not replace the API's documented-use boundary. Check that every endpoint is published for the intended use and whether the policy's AI-call or extraction provisions apply to the design.
SAP API Policy 4.2026a →What it declares
Every artifact
- Type
- IDE extension
- Origin
- By a community org
- Runtime
- Node.js
As an IDE extension
- Installs into
- VS Code
- Distributed through
- Visual Studio Marketplace
For agent guidance
- Installs into
- Claude Code
Connections
Giving an AI agent access to the systems you run over MCP
Packaged knowledge that teaches a coding agent a framework
Editor help with source: completion, navigation, errors, refactoring
Drag-and-drop UI5 view editor that writes an abap2UI5 app class
UI5 apps written as one ABAP class, rendered by a generic shell
HTTP endpoint under /sap/bc/adt exposing ABAP development objects
UI5 apps written as one ABAP class, rendered by a generic shell
VS Code extension editing ABAP systems over ADT as a filesystem
UI5 apps written as one ABAP class, rendered by a generic shell
Download
README
quoted from abap2UI5/vscode-extension under MIT, readabap2UI5 for VS Code
VS Code extension for developing abap2UI5 apps: launch an app with F9, see it right next to the source, and have it reload automatically when you activate the class — without the context switch to the browser. The whole abap2UI5-linter runs in the editor while you type, so a broken view is a squiggle rather than a blank screen.
Works with any system running abap2UI5 (on-premise or cloud). The only thing tying the extension to a system is the launch URL you configure once.
Documentation
→ The extension, in full — F9 and the embedded preview, the auth proxy and why it exists, reload on activation, the view checks and quick fixes, completion and hover for the whole UI5 API, the systemless view preview, the refactorings, the MCP servers, what works in the browser, and every setting.
Installing
Install abap2UI5 from the
VS Code Marketplace:
Extensions panel (Ctrl/Cmd + Shift + X) → search for abap2UI5 →
Install. Through the terminal:
code --install-extension abap2ui5.abap2ui5
On Open VSX for VSCodium,
Eclipse Theia, SAP Business Application Studio and friends. Without Marketplace
access, every
release carries
the .vsix — Extensions panel → … menu → Install from VSIX… — or build
it yourself, see Packaging below.
Coming from a pre-Marketplace
.vsixinstall? Those builds used the placeholder publisherabap2ui5-local, which makes them a different extension to VS Code — they keep working but never update. Uninstall once (code --uninstall-extension abap2ui5-local.abap2ui5), then install from the Marketplace. Settings are kept; the stored SAP credentials are asked for once again.
On the first F9 the extension asks for the launch URL, with {class} as the
placeholder:
https://host:44300/sap/bc/z2ui5?app_start={class}&sap-client=100
What you get
- F9 runs the app next to the source, and the cursor stays where it was. Ctrl+F3 activates the class through your ABAP tooling and reloads the preview — activations done any other way are noticed on the server too.
- The preview is more than a browser tab: device widths, themes and languages; runtime errors forwarded into the editor; a click-the-control → jump-to-the-builder-call inspector; the live JSON model; a traffic log with roundtrip timings; headless screenshots; and a pin that carries the model across a reload.
- Static view checks while you type — the linter's property gate in
process, the abap2UI5 rules with it, the render gate on demand. Quick fixes,
waivers that CI honours, and a findings view grouping the repository's
findings by rule. An open
abaplint.jsoncwhose abap2UI5 pin is below the release the bundled linter assumes gets a warning on itsbranchline. - The whole UI5 API in completion and hover, plus the binding paths your
class's model actually has, and the
client->API with its ABAP signature. - Write the view faster: Format Document repairs a chain's indentation, Emmet-style abbreviations expand into one, Extract to View Method splits a long one, and Convert XML View to Builder Chain turns a demo kit sample into ABAP.
- See the view without a system — Preview View (No System) renders what the class builds, with a device matrix and a compare-with-HEAD mode.
- Test without a system — Run Unit Tests (No System) runs the app's
ABAP Unit tests in the transpiled abap2UI5 backend through
mcp-server's
abap2ui5-unit, in a terminal, for one class or the wholesrc/: the editor's counterpart ofnpm run test:unitin a project from app-template. - Migrate a classic report — Migrate Classic Report to abap2UI5 turns a
*.prog.abapinto an abap-cloud-gui report class with its converter report2cloud, opens the class beside its migration report and puts every statement it refuses into Problems (details). - Navigate an app: the view hierarchy in the Outline pane, Go to Definition
between an event and its
WHEN, F2 renaming every literal an app is wired together with, and a navigation map of the workspace's apps. - Open a class from a link:
vscode://abap2ui5.abap2ui5/open?code=…or?src=<raw GitHub URL>opens the class unsaved, ready for F9. - Templates: New App from Template is a gallery of linter-clean skeletons; New Project from Template writes a whole app-template project.
- For AI agents: the
abap2UI5 MCP server registered for
every MCP client in the window, plus a second, in-extension server holding
what only the extension has — the configured systems, the credentials and the
proxy (
list_systems,search_apps,run_app_on_system), and, once you allow it, the app tools that let an agent operate an app on the system through its JSON protocol. Add Agent Setup to Workspace makes an existing project agent-ready: app-template'sAGENTS.md,CLAUDE.md, the four skills,.mcp.json, the two gates andcheck.yml, from the bundled snapshot - nothing overwritten,package.jsonand.gitignoremerged, the gates pointed at yourSTARTING_FOLDER, the same files asnpm create abap2ui5-app@latest -- --agent-setup. The setup for every agent, the Claude Code plugin among them, is on the AI page of the documentation. - Works in the browser — vscode.dev, github.dev and browser-based SAP Business Application Studio get the language half.
Agent app tools on the system
The abap2UI5 System MCP server offers four tools that operate an abap2UI5 app semantically on the active SAP system: an agent fills fields by model path or label and fires events by name, over the same JSON protocol the browser frontend speaks - no browser, no CSS selector, no screenshot. Every answer is an agent snapshot v1: the fields with their values and whether they are editable, the actions with their arguments, the tables, the messages, some static text.
| Tool | What it does |
|---|---|
app_list | Class names on the system, from the ADT quick search (filter: the start of the name, * as wildcard, default Z; at most 50). A name search: whether a class implements z2ui5_if_app is not checked. |
app_start | Starts app and answers with its snapshot; optional values become pending edits, max_rows sizes the tables (default 20, max 200). |
app_describe | The current snapshot of a session, from memory - nothing is sent. |
app_act | Fills values and fires event (a name or an action id) with args/row, then answers with the new snapshot. Without event the values stay pending, as typing does in the browser. |
They are the same tools, with the same input, snapshot and refusals, as the
sandbox tools of the abap2UI5 MCP server
(see its docs/agent-snapshot.md, at the vendored commit):
an event that is not on the screen, a field that is not editable, a choice
outside its values is refused with what is allowed, and nothing is sent.
The code behind the snapshot is that server's own, vendored into
src/vendor/agent/ (see Development). What differs on a
real system:
- They act for real, as the configured user. An event may save, post or
delete data. So the tools answer only while
abap2ui5.agent.enableAppToolsis on - off by default, and settable in your User settings only, never by a workspace. While it is off they stay listed and answer with how to switch them on, without contacting the system. - The active system. Like
run_app_on_systemthey work on the system Select System made active, through the auth proxy (the credentials stay there; the requests show in the traffic log). The optionalsystemargument ofapp_list/app_startmust name that system - the tools never switch. A session remembers its system, andapp_actrefuses it while another system is active. - The launch URL must carry the class as a query parameter
(
…/sap/bc/z2ui5?app_start={class}&sap-client=100): the tools post to that endpoint without the class, as the browser posts to its own page. They handle what a browser would - a CSRF token layer in front of the system, a stateful session'ssap-contextid, the system's cookies. app_startalso accepts a namespaced class (/ns/cl_app).
Migrating a classic report
Migrate Classic Report to abap2UI5 (Command Palette, or the context menu
of a *.prog.abap in the editor and the Explorer) converts a classic report
into a class of the abap-cloud-gui
addon, which keeps the programming model of a report: the event blocks as
methods, write( ), alv( ) and message( ) as calls. The converter is the
addon's own report2cloud (tools/report2cloud in that repository), the same one the MCP server's migrate_report tool runs. It is not on npm,
so the command runs it from a checkout of the addon:
git clone https://github.com/abap2UI5-addons/abap-cloud-gui
cd abap-cloud-gui && npm ci
and abap2ui5.report2cloud.path pointing at that folder (or the
ABAP_CLOUD_GUI_HOME environment variable the MCP server reads). Without
one the command says so and offers Configure…; a checkout without its
npm ci gets Run npm ci. It runs with VS Code's own Node.js.
- It asks for the class name - report2cloud's default,
zcl_+ the report name, is preselected - and whether to write next to the report or into another folder; existing files of that class are only replaced after you confirm. - The class (
.clas.abap,.clas.xml, the report's local classes as.clas.locals_def/_imp.abap) and<class>.migration.mdare written in abapGit format, with the report's text pool when its.prog.xmllies beside it. The class opens with the migration report beside it - the work list of TODOs, the tables and APIs not released on ABAP Cloud with their successors, and what the list does not carry over. - A statement with no counterpart in a browser app (
CALL SCREEN, a dynpro module, batch input,SUBMIT, native SQL, ...) is refused: each one is an error in Problems, on the statement in the report, and no class is written. Write Partial Result writes the draft anyway, the refused statements marked in it. - When the new class builds a view of its own, Preview View (No System)
and Check Views are offered on it. A plain report class draws its screen
through
z2ui5_cl_cgui_reportand has no view of its own to check.
A namespaced class name (/ns/cl_x) is not offered here - run report2cloud's
CLI for those.
Commands
All of them are in the Command Palette (Ctrl/Cmd + Shift + P) under
abap2UI5; the few with a key are the ones you reach for while typing. This
reference is generated from package.json with npm run commands, and
npm run commands:check fails when the two drift apart.
| Command | Id | Key |
|---|---|---|
| Run App | abap2ui5.run | f9 |
| Reload Preview | abap2ui5.reload | |
| Activate and Reload Preview | abap2ui5.activate | ctrl+f3 / cmd+f3 |
| Run a Recently Launched App | abap2ui5.runRecent | |
| Run an App from the System | abap2ui5.runFromSystem | |
| Show the Preview in the Panel | abap2ui5.previewInPanel | |
| Show the Preview in an Editor Tab | abap2ui5.previewInTab | |
| Go to the Running App | abap2ui5.revealApp | |
| Set Launch URL | abap2ui5.setLaunchUrl | |
| Select System | abap2ui5.selectSystem | |
| Check System Connection | abap2ui5.checkConnection | |
| Clear Stored SAP Credentials | abap2ui5.resetCredentials | |
| New App from Template | abap2ui5.newApp | |
| New Project from Template | abap2ui5.newProject | |
| Add Agent Setup to Workspace | abap2ui5.addAgentSetup | |
| Take App Screenshot | abap2ui5.screenshot | |
| Show Traffic Log | abap2ui5.showTraffic | |
| Convert XML View to Builder Chain | abap2ui5.convertXml | |
| Show App Navigation Map | abap2ui5.showNavMap | |
| Open Project on GitHub | abap2ui5.openHomepage | |
| Check Views (Static) | abap2ui5.checkViews | |
| Check All Views in the Workspace | abap2ui5.checkWorkspace | |
| Open the Rule Documentation | abap2ui5.openRuleDocs | |
| Expand Abbreviation to a Chain | abap2ui5.expandAbbreviation | |
| Extract to View Method | abap2ui5.extractViewMethod | |
| Show Examples for This Control | abap2ui5.showExamples | |
| Fix All View Findings in the Workspace | abap2ui5.fixWorkspace | |
| Rebuild the View-Check Baseline | abap2ui5.updateBaseline | |
| Preview View (No System) | abap2ui5.previewView | |
| Compare View Preview with HEAD | abap2ui5.previewDiff | |
| Show Reconstructed XML View | abap2ui5.showReconstructedXml | |
| Generate Mock Data for This App | abap2ui5.generateMock | |
| Run Unit Tests (No System) | abap2ui5.runUnitTests | |
| Migrate Classic Report to abap2UI5 | abap2ui5.migrateReport | |
| Fix All View Findings in This File | abap2ui5.fixAll | |
| Install Render Gate | abap2ui5.installRenderGate | |
| Refresh the App List | abap2ui5.refreshApps | |
| Run This App | abap2ui5.runApp | |
| Preview This App's View | abap2ui5.previewApp | |
| Check This App's Views | abap2ui5.checkApp | |
| Copy the App's Class Name | abap2ui5.copyAppName | |
| Turn a View-Check Rule Off | abap2ui5.disableRule | |
| Copy Diagnostics for a Bug Report | abap2ui5.copyDiagnostics | |
| Preview Actions | abap2ui5.previewMenu | |
| Apply the Model Document to the Running App | abap2ui5.applyModel | |
| Update Render Gate | abap2ui5.updateRenderGate | |
| Show MCP Status | abap2ui5.showMcpStatus | |
| Add All Findings of This Rule to the Baseline | abap2ui5.baselineRule |
Settings
Everything lives under the abap2ui5. prefix. The full descriptions (with
examples) are in the Settings UI; this reference is generated from
package.json with npm run settings, and npm run settings:check fails
when the two drift apart.
| Setting | Default | Description |
|---|---|---|
abap2ui5.launchUrlTemplate | "" | URL template used to launch an abap2UI5 app. The {class} placeholder is replaced with the (upper-cased) class name. |
abap2ui5.systems | [] | The systems F9 can launch against. The active one is picked with the command "abap2UI5: Select System" and is remembered per window, so two windows can work against two systems at once. Credentials are stored per host, so switching does not ask again. |
abap2ui5.allowUnauthorizedCerts | true | Allow the embedded preview's local auth proxy to talk to systems whose TLS certificate cannot be verified - self-signed certificates, private CAs not in the OS trust store, hostname mismatches. SAP development systems typically serve self-signed certificates, which is why this is on by default. |
abap2ui5.codeLens | true | Show Run, Activate & reload, Check views and Autofix n findings above the class definition of an abap2UI5 app. The autofix lens appears only while the view check has findings it can correct mechanically. |
abap2ui5.openMode | "tab" | How F9 opens the app. |
abap2ui5.reloadOn | "activation" | When the preview of the shown app class reloads by itself. |
abap2ui5.reloadOnSave | Reload the preview when the shown app class is saved. (deprecated) | |
abap2ui5.viewCheck.onSave | true | Run the static view check every time a checkable file is saved: an ABAP class building views with z2ui5_cl_ui5_view_builder, or a raw *.view.xml / *.fragment.xml. Findings appear in the Problems panel. The command "abap2UI5: Check Views (Static)" runs the same check on demand. |
abap2ui5.viewCheck.live | true | Also check while typing, shortly after each pause. Only the bundled property gate runs on a keystroke - it works in-process and needs no I/O; the render gate stays on save and on demand. |
abap2ui5.viewCheck.command | "" | Command that runs the abap2UI5-linter CLI for the render gate - the property gate is bundled with the extension and needs no command. Leave empty for the default: a local checkout under abap2ui5.mcp.reposRoot when present (runs with VS Code's own Node.js), otherwise npx --yes github:abap2UI5/linter at the release commit of the linter this extension bundles. |
abap2ui5.viewCheck.distribution | "" | Which UI5 distribution the target system serves. SAPUI5 ships libraries OpenUI5 does not (sap.ui.comp, sap.suite.*, sap.ushell, sap.fe, sap.viz, ...), so a SmartTable is fine on SAPUI5 and a guaranteed runtime error on OpenUI5. With openui5 such controls are reported as errors, with sapui5 not at all; left unset, they are hints - the same answer a configless abap2ui5lint run gives. |
abap2ui5.viewCheck.minUi5 | "1.71" | The UI5 version to check against - the version your system runs. A control or property introduced after it is reported (it would not exist on your system), and a deprecation is only reported once it is in effect at that version. The metadata itself comes from the bundled snapshot; see the abap2UI5 output channel for the version it was generated from. |
abap2ui5.viewCheck.render | false | Also run the render gate: the reconstructed view is loaded with a real XMLView.create in headless Chromium. Unlike the bundled property gate this needs the external checker - install it once with "abap2UI5: Install Render Gate" (downloads the checker bundle and Chromium, runs with VS Code's own runtime), or provide your own via abap2ui5.viewCheck.command / abap2ui5.mcp.reposRoot. |
abap2ui5.viewCheck.rollingBundle | false | Download the render gate from the linter's rolling bundle instead of the immutable one published for the release commit of the linter version this extension build bundles. |
abap2ui5.viewCheck.allow | [] | Accepted deviations for the property gate, e.g. sap.m.GenericTile.systemInfo - each entry is passed to the checker as --allow. Merges with the allow list of a workspace's abap2ui5lint.jsonc. |
abap2ui5.viewCheck.rules | {} | Which view-check rules are active, and how loudly. A rule id maps to false to switch it off, to "hint" / "warning" / "error" to change its severity, or to { "severity": …, "exclude": […] }. |
abap2ui5.inlineFindings | "problems" | Show the view check's message at the end of the line it concerns, next to the squiggle. A builder chain is long and the Problems panel is far away, so the message is put where you are already looking. |
abap2ui5.inlineSince | true | Show the UI5 version a control or attribute arrived in, at the end of its line - warned when it is above abap2ui5.viewCheck.minUi5. The metadata ships with the extension, so this answers "does my system have this yet?" while you write the line rather than after the check runs. |
abap2ui5.inlineDeprecated | true | Show a control's or attribute's deprecation at the end of its line, with the replacement the UI5 documentation names. Deprecations and abap2ui5.inlineSince share one decoration pass; this switch governs only the deprecation half. |
abap2ui5.inlineRoundtripCost | true | Show what each PUBLIC attribute adds to a roundtrip, next to its declaration. abap2UI5 serializes every public attribute into the model on every roundtrip; the size is measured from the class's own literal seeds, or from a <class>.mock.json when there is one. |
abap2ui5.renamePreview | true | Show the refactor preview when F2 renames an event, a control id or a bound attribute, so every occurrence can be seen before it changes. |
abap2ui5.previewThemes | [] | Additional UI5 themes the preview's theme picker offers, merged after the built-in list - for a custom theme deployed on your system. |
abap2ui5.previewLanguages | [] | Additional logon languages the preview's language picker offers, merged after the built-in list - passed to the app as sap-language. |
abap2ui5.viewPreview.theme | "sap_horizon" | The UI5 theme "abap2UI5: Preview View (No System)" renders in. Any theme name the runtime ships, e.g. sap_horizon, sap_horizon_dark, sap_fiori_3, sap_belize. |
abap2ui5.viewPreview.viewport | "1280x900" | The viewport(s) the systemless preview renders at, <width>x<height> in CSS pixels - e.g. 390x844 to see the view the way a phone lays it out. Several, comma-separated, become a device matrix: 390x844,1280x900 renders both in one browser session and shows them side by side. The picture is taken full-page, so a view taller than the viewport is shown whole. |
abap2ui5.mcp.enabled | true | Offer the abap2UI5 MCP server to MCP clients in this window (Copilot agent mode and others). The server gives AI agents the abap2UI5 dev loop without an SAP system: capability queries, static view validation, deploy, build, headless run with screenshot. |
abap2ui5.mcp.system | true | Also offer the abap2UI5 System MCP server: real-system tools hosted by the extension itself - list the configured systems, search classes on the system (ADT quick search), run an app through the auth proxy with a headless screenshot, and - only with abap2ui5.agent.enableAppTools - operate an app through its JSON protocol. Uses the same credentials the preview stores; the run tool needs the render gate's Chromium. |
abap2ui5.agent.enableAppTools | false | Allow the abap2UI5 System MCP server's app tools (app_list, app_start, app_describe, app_act) to operate abap2UI5 apps on the real active system: an AI agent fills fields and fires events through the abap2UI5 JSON protocol, as the configured user - an event runs for real and may save, post or delete data. Off by default; while it is off the tools stay listed and answer with how to allow them, sending nothing. User settings only: a workspace cannot turn it on. |
abap2ui5.mcp.command | "" | Command that starts the MCP server. Leave empty for the default: a local checkout under abap2ui5.mcp.reposRoot when present, otherwise npx --yes -p @abap2ui5/mcp-server abap2ui5-mcp. |
abap2ui5.mcp.reposRoot | "" | Folder containing the checkouts the MCP server orchestrates (abap2UI5, samples-controls, samples, samples-stack, and optionally linter, mcp-server, abap-cloud-gui). The matching A2UI5_HOME / SAMPLES_CONTROLS_HOME / SAMPLES_HOME / SAMPLES_STACK_HOME / AI_VIEW_CHECK_HOME / ABAP_CLOUD_GUI_HOME environment variables are passed to the server, and local mcp-server / linter checkouts found here are preferred over downloading via npx. |
abap2ui5.report2cloud.path | "" | Folder of an abap-cloud-gui checkout (with npm ci done) whose tools/report2cloud converter "abap2UI5: Migrate Classic Report to abap2UI5" runs - report2cloud is not on npm. Leave empty to use the checkout ABAP_CLOUD_GUI_HOME names, the variable the MCP server's migrate_report reads for the same purpose. |
Development
npm install
npm run compile # builds dist/extension.js with esbuild
Open this repository in VS Code and press F5 → a second VS Code window (Extension Development Host) starts with the extension loaded.
Handy while developing: npm run watch rebuilds on every change,
npm run lint runs tsc --noEmit plus eslint, and npm test runs the unit
suite with node --test — several hundred tests over every vscode-free
module (bundled with the same esbuild config the extension ships with), plus
the cross-checks: the manifest against the registered commands, the property
gate against the linter's own pipeline, the snippets and templates through the
bundled linter. The in-host smoke tests (npm run test:web,
npm run test:desktop) each download a VS Code build and run in CI.
The agent app tools' snapshot code is
mcp-server's, vendored at a
recorded commit: npm run agent-vendor -- /path/to/mcp-server [--ref <commit>]
copies it into src/vendor/agent/ (and the recorded sessions its tests replay
into src/test/fixtures/agent/), and npm run agent-vendor:check -- /path/to/mcp-server
fails when the copies drift from that commit. npm test fails on a hand edit
of a copy.
Packaging as a .vsix
npm install
npm run vsix
The result is a file such as abap2ui5-<version>.vsix.
vsceis included as a devDependency, sonpm run vsixuses the local version. Alternatively install it globally:npm install -g @vscode/vsce.
Every push and pull request builds the same .vsix in CI and attaches it to
the run as an artifact — handy for trying out a branch without building it
locally.
Releasing
Bump version in package.json, add the matching CHANGELOG.md section, then
either
-
run the Release workflow from the Actions tab — it tags the current commit with
v<version>and releases it, or -
tag the commit yourself:
git tag v<version> git push origin v<version>
Either way the workflow builds the .vsix, creates the GitHub release and
attaches the file, with the changelog section of that version as the release
notes. Tag and package.json have to agree, otherwise the run fails on
purpose — and a version that is already released is refused instead of
overwritten.
Contributing
This project is English-only. Code, comments, identifiers, commit messages, documentation, and every user-facing string in the extension are written in English — see AGENTS.md for the full conventions.
License
MIT — see LICENSE.
History
Other 22
Notes
The bundled linter moves to v0.8.5, and is now taken from its npm release rather than from a commit on its main branch.
-
The extension activates with a third less to load. The project template (300 KB) and the
client->method reference (59 KB) are no longer part of the code every window parses on activation: they ship next to it and are read the first time "New Project from Template", "Add Agent Setup to Workspace" or aclient->completion needs them. The archive library the render-gate installer uses (86 KB) is loaded the first time that installer runs, and the client behind the agent app tools (50 KB) the first time an agent calls one. The activation bundle went from 1.1 MB to 0.62 MB, the vscode.dev bundle from 0.75 MB to 0.39 MB; nothing you can do changed. -
Less work on every keystroke and save. The property gate runs once per edit: the lightbulb and the lenses asked for the findings of the new text before the live check ran, and the check then ran the same gate over the same text again. Whether a file is one the view check judges is decided once per edit instead of on every keystroke (for a class that is not an app, that decision read the repository config's timestamps each time); the lenses above a class no longer re-evaluate on every diagnostic another extension publishes, only when one of abap2UI5's own findings moved, and the "Run unit tests" lens no longer looks for the test include on every pass; the reconstructed XML preview reuses the reconstruction the editor already has instead of parsing the class a second time. The first scan of a workspace reads its classes twelve at a time instead of one after the other, and the web build (vscode.dev, the browser-based Business Application Studio) registers its features first and reads its 600 KB of data after, concurrently - a command or a view used to wait for the slowest read; a check asked for before the data is in waits for it.
-
A window without ABAP in it no longer scans the workspace on activation. The extension activates for any XML file; the index of app classes (what lets F9, the lenses and the Apps tree recognise a class that inherits
z2ui5_if_app) is now built when the first ABAP document shows up, in slices that leave the editor responsive, and a change on disk re-reads that one file instead of everything. The Apps tree is a view of that index, so a save moves one node rather than re-reading the workspace - and a save or an open that changes nothing the tree shows (the usual one) does not re-render it at all - and the text the extension keeps of your classes is validated cheaply when it is old (a stat per file, never a second read of the whole workspace) and let go when it has not been used for a while. A class whose file is deleted, or a file closed after an unsaved rename, leaves the Apps tree at once rather than at the next full rescan. -
Add Agent Setup to Workspace. A new command makes the project in the open folder ready for an AI agent - most abap2UI5 projects did not start from app-template and have no briefing for one. It adds what
npm create abap2ui5-app@latest -- --agent-setupadds, from the copy of app-template bundled with the extension, so it needs no network and works on vscode.dev too:AGENTS.md,CLAUDE.md,.claude/settings.jsonand the four skills,.mcp.json,abaplint.jsoncandabap2ui5lint.jsonc,.github/workflows/check.ymlwith the two scripts it runs, and.nvmrc. A file you already have is left as it is;package.jsonand.gitignoreonly gain the scripts, devDependencies and patterns you are missing, and every value of yours that differs from the template's is named. The gates are pointed at the folder your.abapgit.xmlnames asSTARTING_FOLDER, and nothing is written into it. A confirmation lists every file first; the abap2UI5 Agent Setup output then says what was done and what to run next (npm install,npm run check) - and how the Claude Code plugin brings the skills and the MCP server without the gates. The Get started walkthrough has a step for it. -
"New Project from Template" is in step with app-template again: its latest
AGENTS.md,abap-checkandui5-checkskills,check-pin.mjsanddoctor.mjs. -
The MCP walkthrough step names the abap2UI5 Claude Code plugin as the alternative for Claude Code users, gives the
claude mcp addline for the server alone, and says the server is listed in the official MCP Registry from 1.0 on; the README links the documentation's AI page. Hover and completion describe the client API with the core's current wording. -
The agent app tools follow the abap2UI5 protocol's frontend rules. The vendored agent client is now abap2UI5/mcp-server
a4d9f07: a response of another protocol number is refused with both numbers named instead of being shown; anapp_actwith an event while another is still running waits for it instead of sending the same draft id twice; values set meanwhile are kept; a popup of the previous app closes when another app answers; and the system's error text is shown exactly as sent (a<b>in it stays visible text). The CSRF token handshake and the stateful session id (sap-contextid) are now done by the client itself - the extension's transport through the auth proxy only carries their headers and the system's cookies, so the token is fetched once and the body re-sent once, never twice. -
The agent app tools take mcp-server's later fixes. The vendored agent client is now abap2UI5/mcp-server
185baed: a binding written as{path: '/A', model: 'other'}is read as the named model's, so a value is no longer written into the app's own model; an unsent edit no longer survives into a view the system displayed anew, and a model push no longer invents table rows for one; a model path through__proto__,constructororprototypeis refused, and an element of that name no longer breaks the screen; a number field takes a number or a decimal string, not whateverNumber()makes of a value; an action list that is no list is refused with a sentence instead of "object is not iterable". -
Migrate Classic Report to abap2UI5. A new command - in the Command Palette and in the context menu of a
*.prog.abap- converts a classic ABAP report into an abap-cloud-gui report class with that addon's converter, report2cloud: you name the class and pick the folder, and the class opens beside its migration report (the TODOs, the tables and APIs to replace for ABAP Cloud). A statement that has no counterpart in a browser app -CALL SCREEN, batch input,SUBMIT, native SQL - is reported in Problems on the line it stands on, and Write Partial Result writes the draft with those statements marked. report2cloud is not on npm: point the new settingabap2ui5.report2cloud.pathat a checkout of abap2UI5-addons/abap-cloud-gui withnpm cidone (or setABAP_CLOUD_GUI_HOME); the command offers Configure… until you have. -
AI agents can operate an app on the system - once you allow it. The abap2UI5 System MCP server gets the four app tools of the abap2UI5 MCP server, on your real system:
app_listsearches class names,app_startstarts an app and answers with an agent snapshot (its fields and values, its actions, tables and messages),app_actfills fields and fires an event,app_describerepeats the last snapshot. An agent works by model path, label and event name through the abap2UI5 JSON protocol - no browser, no screenshot - and anything not on the screen is refused with what is. They run through the auth proxy on the active system, as you: an event may save, post or delete data. That is why they do nothing until you setabap2ui5.agent.enableAppToolsin your User settings (a workspace cannot); until then they tell the agent how to switch them on. A value help (SelectDialog,TableSelectDialog) is a table of the snapshot, andapp_actwithrowon itsconfirmpicks that row as a click does; the items of aMessagePopover/MessageVieware listed as messages. -
Security:
- Render gate. Its last-resort
npxfallback runs the linter release this extension bundles instead of the linter's current main, and the render gate does not run in an untrusted workspace (Restricted Mode).abap2ui5.viewCheck.rendercan no longer be switched on by an untrusted workspace's settings. abap2ui5.viewCheck.rollingBundleis read from your user settings only: a repository's.vscode/settings.jsoncan no longer make Install Render Gate download the linter's rolling build instead of the release this extension bundles. Off by default, as before.abap2ui5.allowUnauthorizedCertsis read from your user settings only: a repository's.vscode/settings.jsoncan no longer switch certificate verification for the auth proxy on or off. Its default is unchanged; a value you had set in a workspace's settings no longer applies - set it in your User settings instead.- Baseline. The "add … to the baseline" quick fix, Add All Findings of
This Rule to the Baseline and Rebuild the View-Check Baseline
refuse - with a message saying why - a
baselineinabap2ui5lint.jsoncthat points outside the workspace folder holding the config, a symbolic link out of it included - and a link to a file that does not exist yet, which writing through would have created wherever it points. They used to overwrite whatever JSON file the config named. - Add Agent Setup to Workspace writes nothing through a symbolic link:
a project whose
.claude(orpackage.json, or any other file of the setup) is a link has that file skipped and named in the confirmation. A cloned repository carrying.claude -> ~/.claudeused to have the template's permission allowlist written into your global Claude Code settings.
- Render gate. Its last-resort
-
View check - the editor agrees with CI in more places:
- Disable directives follow the
rulesblock. A repository that switchedunused-directiveoff, re-gradedunknown-directive-ruleor excluded a folder from them still saw them in the editor; and a waiver ofunused-namespace-declarationon a class the rule does not judge was reported as unused. unused-namespace-declarationis no longer reported (nor its fix offered, which deletes the declaration) on a class that writes the prefix in a part of the view the check cannot reconstruct - CI has been silent about those, because the view needs the declaration.- On vscode.dev / in the browser the icon rules (
unknown-icon,icon-too-new,icon-removed) report again - the linter's icon list could not be read there - and abaselinewritten as./../baseline.json,lint/../baseline.jsonor an absolute path is found again (only a leading../used to be resolved, so the findings it waives were reported). - "Fix crlf-line-ending" converts the file now. The linter's fix deletes
every
\r, and an editor position cannot address the gap between\rand\n- so the quick fix, Fix All and the workspace fix applied empty edits and the warning stayed. They now switch the document's line endings to LF, in the same edit (and undo step) as the other fixes. - Quick-fix titles name a line, not a number. For the line-keyed rules
(
trailing-whitespace,source-line-too-long) the lightbulb said "fix trailing-whitespace on 12" - or, with the next linter release, "on 3", the count of blanks. It now says "on line 12"; a fix over many lines names none. - An app class whose view comes from another class is checked, as CI
checks it: by the rules that read the class rather than a view - a
_bind( )on a private attribute, an obsolete call, the event wires, the lifecycle dispatcher. The editor used to say "nothing to check". Preview View (No System) and Generate Mock Data for This App still ask for a class that builds its view itself. allClassesandpropertiesinabap2ui5lint.jsoncare honoured. WithallClasses, every class is checked - one that builds no view by the source rules, as CI does - and Check All Views in the Workspace reaches it. Withproperties: falsethe walk over the view against the UI5 metadata is skipped as in CI (in an XML view, the whole check), and a waiver of one of its rules is no longer reported as unused.- An icon in a commented-out control of an XML view is no longer reported - CI reads the view with its comments blanked.
- The
check_view_sourcetool of the abap2UI5 System MCP server treats an unsetabap2ui5.viewCheck.distributionas "not decided", like the editor - it judged as SAPUI5 and so hid the SAPUI5-only-control hint. - Ready for the next linter release:
rows-hidden-by-visible,enum-bound-to-initial-field,navigation-lost-on-rebuildand the opt-inportable-app(its profile ships with the extension) are judged with the same inputs CI gives them as soon as the bundled linter brings them - the start-path model, whether the class raises the size limit, the pages of a container, the portable profile. - Fix All View Findings in the Workspace counts what it fixed in findings, and says separately how many files it changed to LF line endings - it used to add up its edits, a finding fixed in two places twice and each line-ending change as one more "fix".
- A waiver in an XML view never goes above its XML declaration (nor in
front of a byte-order mark). With the root on the declaration's line (a
minified or generated view), the "suppress on this line" quick fix wrote
its comment in front of
<?xml …?>- which every XML parser, UI5's included, refuses, so the whole view stopped loading. The directive now goes right behind the declaration.
- Disable directives follow the
-
XML views:
- Namespaces are read per element, as XML scopes them: an inner
<VBox xmlns="sap.ui.layout.form">re-resolved every unprefixed tag of the view for completion, hover and the colour swatches, a prefix declared in one subtree was offered in another, and a declaration in a comment counted. Namespaces declared with single quotes (xmlns:m='sap.m') are read too, and so is a single-quoted colour value. @sinceand deprecations inline, as in a builder chain: the UI5 version a control or attribute arrived in, warned aboveabap2ui5.viewCheck.minUi5, and the deprecation with its replacement.id,classandbinding- on nearly every control of a real view, but declared in no control's metadata - are offered and explained, in the XML and in the builder chain alike.- Completion offers nothing in front of the first tag (accepting an
offer there replaced the root tag's name) or inside a comment (a
<word>in the prose was taken for a tag). - Convert XML View to Builder Chain puts no line break inside a literal
from a CRLF view: a long binding written over several lines is split
into
&&chunks, and the cut could fall between\rand\n, leaving a bare\rthat abapGit refuses. Attribute values are read the way an XML parser reads them: each line break and tab a blank, a still a line break.
- Namespaces are read per element, as XML scopes them: an inner
-
Rename, navigation and events:
- F2 on an event renames only the
WHENbranches of the class'sCASE client->get_event( )(orclient->get( )-event) - it used to rewrite a same-namedWHEN 'EDIT'of an unrelatedCASE mv_mode.or of a status switch nested in a handler too. Go to Definition, the highlights, the "raised n× in the view" lens and completion in aWHENfollow the same rule. A class without such a CASE keeps the old behaviour. - F2, Go to Definition and the highlights on an event answer only on
the event's name in
_event( )- positional orval =. In any other literal of the call (arg = \{parameters>/value}`, at_argrow) F2 offered a rename that then changed nothing, and an argument spelled like an event jumped to that event'sWHEN. The same goes for aWHENvalue no event is spelled like (``WHENOut of StockTHEN`` in aSWITCH, ``WHEN%MSG` ``). - F2 on a bound attribute no longer rewrites the word inside a string
template's text (
|The mv_title is …|), another structure's component embedded in one (|{ ls_row-mv_title }|) or a path into another model: a class with aDATA valueorDATA texthadarg = \{parameters>/value}`,{source>/text}and{device>/text}renamed along with the attribute, which broke the event parameter or the device binding without a word.|{ mv_title }|` is still renamed. - Control ids.
a( n = `id` v = |inp_{ lv_i }| )no longer makes the next attribute's name (value) a declared id - the id is the literal right behindv =, or there is none. - Go to Definition and Go to Symbol in Workspace land on the method's
name in a class written in lower case:
method meth.used to put the cursor on themethof the keyword. - Outline. A very large class (from about 120,000 method calls on) no longer makes the view outline, and with it the breadcrumbs, fail.
- F2 on an event renames only the
-
Preview and systems:
- Reload on activation. Saving right after the preview reloaded (before the system had answered which version the preview now shows) could reload the preview again on the first poll for an activation that never happened, and clear the not activated badge. The watch now waits for the real activation.
- Preview theme and language. Switching them changes only the
sap-ui-theme/sap-languageparameter of the launch URL: the other parameters stay exactly as configured. Every launch used to re-encode the whole query (%20became+,~became%7E,/and:were escaped, a bare?debugbecamedebug=). - Proxy. A page declared as
windows-1252oriso-8859-1shows its euro signs, dashes and curly quotes the way a browser does, a page in another charset (iso-8859-2,shift_jis, ...) is decoded as that charset instead of as UTF-8, and a page in a charset that cannot be decoded is passed through unchanged instead of being garbled. abap2ui5.systemsentries of the wrong type ("name": 100, aurlthat is not a string, a bare string in the list, an object instead of the list) are skipped like any other malformed entry; they used to make Run App, the system picker and the status bar fail with an error until the setting was fixed by hand.- Preview View (No System) shows why a render could not even start (a temporary folder that could not be written, a checker that could not be resolved) in the panel, over the last pictures. It used to keep saying "rendering…" until the next save, with the reason only in the log.
-
Editing, projects and agents:
- Control Properties view. Removing the last attribute of a chain left
its closing
).on a line of its own; the paren now moves up to the line of the call that is the last one ()->tag( \Input` ).`), unless that line ends in a comment. Removing an attribute followed by a blank line no longer takes the blank line with it. - Builder-chain abbreviations (
Page>content>Button+Input) expand when a text or a value holds a lone parenthesis -Text{1) First}+ButtonorInput[placeholder="(optional"]+Buttonused to be refused as "not an abbreviation". - Framework-pin warning. A pin kept in a comment of
abaplint.jsonc(// "branch": "1.100.0"above the real one, or a dependency commented out) is no longer read as the pin, and a{in a comment no longer hides the dependency it sits in. - Add Agent Setup to Workspace says that a
package.jsonholdingnull, an array or a plain value is not a package manifest, instead of calling it invalid JSON - and no longer plans to add entries to an array-shaped one that it would have written back unchanged. - New Project from Template and Add Agent Setup to Workspace keep the
package.jsonname within npm's rules: a folder name longer than 214 characters is cut, and a folder callednode_modulesorfavicon.icogetsabap2ui5-app-npm installrefused the names they used to write. - Opening a workspace reads its classes once. The app index, the class index the view check uses and the apps tree each scanned the workspace when the window opened, and the three scans ran at the same time - so every class was read three times. They now share one scan (over samples-controls' 644 classes: one read per file instead of three, about half the time).
- The MCP server finds abap-cloud-gui under the repos root. An
abap-cloud-guicheckout underabap2ui5.mcp.reposRootis now handed to the server asABAP_CLOUD_GUI_HOME, as the other checkouts are. The server'smigrate_reporttool runs that addon's converter from a local checkout only, and a server started through npx does not look beside your repos root - so the agent's tool reported the checkout missing while Migrate Classic Report to abap2UI5 found it.
- Control Properties view. Removing the last attribute of a chain left
its closing
-
Internal: TypeScript 6.0 for the type check (7.x waits for typescript-eslint, whose peer range ends below 6.1), eslint 10.12, esbuild 0.28.2, and
@types/nodeback on the Node 22 line thatengines.nodenames. -
The view check runs abap2UI5-linter 0.8.5 (from 0.8.3). What that brings to the editor: a builder attribute written as
a( v = … n = … )is reconstructed likea( n = … v = … )- its value, often a binding, used to be dropped and was missing from every check of the class; and a raw*.view.xmlwith a duplicate attribute, a mismatched or missing closing tag is reported asmalformed-xmlinstead of passing. The render gate, in its matching bundle, renders a view that uses the framework'sz2ui5/model/clipboardmodule (sap.m.plugins.CopyProvider) instead of failing to create it. -
The render gate downloads the bundle of exactly that release. The linter publishes its checker bundle per commit, so the extension records the commit each bundled version was published from and fetches that bundle. A build whose bundle is not published says which release it wanted; Update Render Gate and the diagnostics report name the bundled linter version and its commit instead of a bare commit hash.
-
Fixes from a review of the whole extension:
- Preview and activation. An app's screenshot (and MCP's
run_app_on_system) no longer fails its first roundtrip with a 404. Ctrl+F3 whose activation failed no longer reloads and stops watching - the preview keeps its "not activated" badge and reloads once the activation does happen; a reload without an activation command, or after re-entering credentials, keeps the watch too. The watch's reload returns the cursor to where you are, not to where you pressed F9. Set Launch URL edits the active system in place - it used to drop a same-named second system or add a duplicate. - Proxy. A connection that breaks mid-download aborts the resource
instead of completing it with an error text; a WebSocket the system
never answers times out; a page that declares its charset only in
<meta>keeps its umlauts; a second Content-Security-Policy keeps all of its directives. A wrong password no longer costs extra failed logons through the UI5-version probe. - View check. The read-only side of a git diff is no longer checked
(findings showed twice, and Fix All tried to edit it). A change to a
base config pulled in through
extendsis picked up without touching the extending file. Turn off rule writes only the workspace's own rules, and Undo restores them exactly. Add all findings to the baseline writes the file once instead of once per finding. Preview Diff finds the committed version when the workspace folder is below the repository root. A proxy's block page downloaded instead of the render-gate bundle no longer blocks the real bundle afterwards, and two windows installing the gate at once no longer break each other's install. - Editing. Extract to View Method no longer breaks the class when a
comment sits above the chain. Removing an attribute whose value wraps
onto a second line removes all of it. The property editor no longer
treats
`sap-icon://` && mv_iconas an editable literal. Renaming an attribute (F2) leaves same-named structure components alone. The quick fix for an unhandled event is offered when a handler usesIGNORING CASEorTO UPPER CASE. Hover in an XML view works over the whole attribute name.CLASS-DATAandREF TOattributes no longer get a roundtrip-cost annotation. - Apps tree, CodeLens and navigation. Opening a test include no longer
makes subclasses of that class lose their CodeLens and their entry in
the apps tree, and both update once a newly pulled base class is
indexed. The navigation map follows
CAST #( … )andCONV #( … ). Show Examples no longer counts alabelaggregation as a use ofLabel, and a broken catalogue download is retried instead of cached as empty for a day. New Project from Template refuses class names the new project's own abaplint rejects (it expectsZCL_/ZCX_). On Windows the unit tests run incmd.exe, the shell their command line is quoted for. Convert XML View keeps a warning about an unreadable tag from spilling into the generated ABAP. - Preview, model and navigation map. Apply the Model Document to the
Running App confirms only what the preview accepted; it used to say
"applied" even when no app was loaded or nothing of the model was in the
document, and now says why instead. The theme and language pickers in a
running preview follow a change to
abap2ui5.previewThemes/abap2ui5.previewLanguageswithout an F9. Show the Preview in an Editor Tab / … in the Panel reads the same in the preview menu as in the Command Palette, and honours anopenModeset per workspace folder. The activate badge and the welcome screen say that Ctrl+F3 is bound in ADT documents only - elsewhere the button or the command Activate and Reload Preview does the same. Show App Navigation Map run again while its scan is still going no longer opens a second map. The settings UI showedabap2ui5.reloadOnSaveastrueby default; it has no default (the effective behaviour isreloadOn: activation). A screenshot's Chromium runs in a throwaway profile that is deleted afterwards, instead of leaving the proxy's session cookie in the user's default profile. Take App Screenshot after the proxy moved to another system (a connection check, an agent's connect) says so instead of photographing a connection-refused page. - System and credentials. An agent that retries
search_appsorrun_app_on_systemon a rejected logon no longer re-sends the password to the system on every call - the proxy's stop after a 401 now holds for those two tools as it does for the app tools, so a wrong password costs one failed logon, not a locked user. A 403 from the system (a missing authorization, or the CSRF handshake an agent's first POST gets) is logged and no longer offers Re-enter Credentials, which deleted a password that worked. Check System Connection reports a proxy that could not listen as the proxy's failure, not as an unreachable host. - View check. Fix Workspace and Check Workspace no longer apply a
file's fixes to a buffer edited while the sweep was reading it - the
guard compared the right version against the wrong one. The live check
leaves a class under a dot-directory or one the config's
ignoreprunes alone, as CI does; it used to squiggle it, the sweep then removed the findings as stale, and the next keystroke brought them back. On vscode.dev the workspace sweep skips those files too, reports progress every 25 files rather than per file, and agit pulltouching a config and its baselines re-reads them once rather than once per file. A document that can never be checked (a log, a JSON file) no longer costs its whole text per keystroke to find that out. - Classes the extension knows about. The class index and the app-class index read every class of the workspace; both stopped at 2000 files and judged a larger workspace against a partial index without saying so. A workspace folder added while a class file was changing is indexed (the file's event used to cancel the folder's rebuild), and a class created on disk during the first scan is in the index when the scan is through.
- Render gate. For the per-commit bundle URL a changed download is refused even when a published checksum agrees with the new bytes - that checksum sits in the same release, and the digest this machine verified is the anchor. The rolling bundle, which moves by design, still trusts the published checksum.
- Preview and activation. An app's screenshot (and MCP's
-
The Workspace Trust text no longer claims that every restricted setting is read from the user settings only.
abap2ui5.systems,abap2ui5.launchUrlTemplateandabap2ui5.viewCheck.renderare ignored from the workspace in Restricted Mode, and a trusted workspace can set them - a team shares its systems that way. The text now says so; the settings themselves are unchanged. -
Internal:
@abap2ui5/linteris an exact npm devDependency, like in every other repository of the ecosystem, instead of agithub:commit pin; the weekly bump follows the latest published version, and a test holds the manifest, the lock and the recorded release commit together.
Other 5
Notes
- The MCP server starts again without a local checkout. The default
command was
npx --yes @abap2ui5/mcp-server; since the server's 0.2.0 release the package has two commands, and npx stopped with "could not determine executable to run". The extension now startsnpx --yes -p @abap2ui5/mcp-server abap2ui5-mcp, which works for every release. - The view check no longer calls a view "rendered" that was not. A
checker without its UI5 runtime (
@abap2ui5/linter-render) skips the render gate silently and reports a clean result. The extension now reads the report's counters and says that the render half did not run, instead of showing the view as passed. - Format Document keeps a file's CRLF line endings. The builder-chain
formatter applied the linter's layout fixes verbatim, and those always use
\n; on a CRLF file that left the re-indented chain lines with mixed line endings, and on an already-formatted CRLF file it reported edits that stripped the\roff every chain line. The formatter now writes the fixes in the document's own line ending. - The view check no longer rewrites a repository's badge file. The render
gate and the systemless view preview run over a scratch copy of the buffer,
and the checker discovered the repository's
abap2ui5lint.jsoncfrom the working directory - so a config with abadgehad that badge overwritten with a one-file verdict on every save, and a config anywhere above the temporary directory could steer the run. Both now pass--no-config; the repository's rules still govern the check, applied by the extension itself. - No console windows on Windows. The extension host has no console
there, so every command the extension ran - the view check, the unit
tests, the gates, and the
taskkillthat ends one - opened a console window of its own for as long as it ran. They are started hidden.
- The MCP server starts again without a local checkout. The default
command was
Other 14
Notes
The bundled linter moves to v0.8.3, the templates and snippets are brought in line with what that linter teaches, and the snippet set grows by five.
- The view check knows 23 more rules - the bundled
@abap2ui5/lintermoves from 0.6.1 to 0.7.0 (its metadata to OpenUI5 1.152.0). Among what the editor now reports live and on save:second-root(a second root element beside themvc:View, the one chain shape that silently builds a broken view),message-box-removed-parameter,unbound-public-attribute,client-handle-capture(a_bind( )/_event( )handle captured into a variable),event-arg-single-row-table,event-arg-default-index,unescaped-text-in-attribute,redundant-serializable,bound-aggregation-without-templateandcolumn-cell-count-mismatch. Most of them carry fixes, so they arrive as Quick Fixes and in Autofix too. - Fewer false positives - the pin moves on to the linter's v0.8.1
release.
handler-without-eventcounts only theWHENs of aCASEover the event and knows theonCloseoptions andhash_attach_changed;unbound-public-attributereports the right line after aTYPES BEGIN OFand no longer reports aTYPE REF TOattribute; a literalb = abap_falseis read as false;smart-variant-without-initacceptsfilter_bar_variant_init;frontend-action-as-backend-eventignores a class-localcs_event;missing-on-navigated-branchno longer fires on a sub-app; and theunused-namespace-declarationQuick Fix handles the last declaration of a chain. A disable directive written for one of those is now reported asunused-directive. - Obsolete companion controls are errors - the pin moves on to v0.8.2.
The new rule
obsolete-custom-controlreports the eightz2ui5.cccontrols abap2UI5 marks obsolete -Timer,Focus,Scrolling,Title,LPTitle,Favicon,InfoandHistory- and names the frontend event or client call that replaces each (aTimerbecomesfollow_up_action( val = client->cs_event-start_timer … )). It reads a builder chain, an XML view and, on a class still on the frozenz2ui5_cl_xml_view, the old_z2ui5( )->timer( )helpers, which the editor now reports besidefrozen-view-builderexactly as CI does. The render check also knowsInputExt,UploadSetExtandSmartMultiInputExt: a view using them renders instead of failing to load the control, and a misspelt attribute on a companion control is reported with a did-you-mean (inputmode→inputMode). - A variable typed outside the class no longer breaks the render check -
the pin moves on to v0.8.3. A bound attribute whose type the class does
not declare (a DDIC type, or one another class owns, such as
zcl_x=>ty_t_token) used to reach the render check as an empty string, and a property that is not a string rejected it: the view failed to load in the editor although it runs. It is now left out, and the control keeps its default. - The framework-pin warning is live. The linter now ships its
compatibility record (
@abap2ui5/linter/compat), so anabaplint.jsoncpinned to an abap2UI5 release below what the bundled linter assumes gets the warning on itsbranchline that 0.29.0 prepared and no pin could trigger. On vscode.dev the enum-key table the linter grew for the eight enums whose key and value differ is now handed on like the other hidden tables, so the web gate judges those values as the desktop does. - The gallery templates dispatch the way the app guide does. "Form" and
"Popup" lost the
check_on_init( )arm that only repeated thecheck_on_navigated( )display (redundant-init-display- init implies navigated); "List" and "Master & detail" keep theirs, because it seeds data. All templates and thez2ui5eventsnippet dispatch onCASE client->get_event( ).- the spelling the guide and thez2ui5testdouble use, so a test written from the snippet actually reaches aWHENbranch. "Master & detail" wires its one event argument asarg = ...instead of a one-rowt_argtable. - Snippets follow the house chain layout.
z2ui5app,z2ui5table,z2ui5popupandz2ui5popoverhad chains Format Document rewrote the moment they were inserted (one call per line, four spaces per level, anend( )in the column of the element it closes); they now insert what the formatter would produce,z2ui5eleinserts a container with one child closed in the container's column, and every chain snippet leaves the cursor in the column of the control it just built, so the next->tag( )is a sibling at the sibling's column. The suite now runs every snippet and template through the linter withchain-house-layoutswitched on, so the drift cannot come back. - Three snippets corrected.
z2ui5eventarginsertsget_event_arg( )(the index is an optional placeholder - the linter'sevent-arg-default-indexreports a spelled-out1),z2ui5navbackinserts the parameterlessnav_app_leave( )the interface documents as the return to the caller, andz2ui5eventis theget_event( )form above. - Five new snippets for guide idioms:
z2ui5bool(an attribute renderedtrue/falsefrom an ABAP flag,b =instead ofv =),z2ui5list(a List whose row press carries the row's key as a singlearg),z2ui5liveevent(an Input whoseliveChangeround-trips per keystroke withcheck_queue_lastandcheck_no_busy, so nothing typed is lost and no busy overlay flashes),z2ui5backbutton(showNavButtonbound tocheck_app_prev_stack( )plusnavButtonPresswired to_event_nav_app_leave( )) andz2ui5popupclosewire(a Close button whosefollow_up_action( cs_event-popup_close )tears the popup down without a roundtrip). - The bundled
client->reference and the app-template snapshot are regenerated (follow_up_actionand_eventdocs, the template's skills). - The view outline and completion follow handle variables. A class
written the way the starter template is -
DATA(page) = view->ele( \Shell` )->ele( `Page` ), thenpage->ele( `List` )..., thenpage->tag( `Button` )- is now read the way the builder and the view check read it: the Button is a child of the Page, not of the List above it. Binding completion at Page level no longer offers the list row's fields (which the check then reported asunknown-binding-path), andpage->a( n = `...` )` offers the Page's members. - **Completion inside
client->_event( \...` ).** The event names the class already handles - itsWHENbranches andcheck_on_event( )tests - come first, then the names its other wires raise; a handler spelled differently (WHEN `SAVE`for asavewire) is named in the entry, so the mismatchevent-without-handler` reports later is avoided at the keystroke. - Completion after
client->_bind(,_bind_edit(and_bind_path(. Offers the PUBLIC instance attributes of the class - the only data the framework can bind - with tables marked as what an aggregation binds;CONSTANTSandCLASS-DATA, which raiseBINDING_ERRORat runtime, are left out. Works for theval =form and in any spelling. - Internal: the gate parity test normalises line endings in the linter
fixtures it reads, so the Windows CI job no longer fails on the CRLF an
autocrlfcheckout gives them.
- The view check knows 23 more rules - the bundled
Other 8
Notes
Four additions around the dev loop: a fix for the most common dead-control finding, mock data from the class, editing the running app's model, and a test skeleton - and "New Project from Template" now writes the template's whole project.
-
The scaffolded CI workflow runs the app's ABAP Unit tests without a system. app-template's
check.ymlgained aunitjob (theabap2UI5/mcp-serveraction: the framework at the pinned release, its transpiled backend, the tests through the open-abap runtime), and the workflow "New Project from Template" writes carries the same job on the same pin, plusnpm run test:unitfor the terminal. -
New command "Run Unit Tests (No System)" (
abap2ui5.runUnitTests, in the palette, the editor's abap2UI5 menu and the apps tree, and as a "Run unit tests" lens above a class that has a*.clas.testclasses.abapnext to it). It runs the sameabap2ui5-unitthe scaffolded CI job andnpm run test:unitrun - the framework at the release the project'sabaplint.jsoncpins, its transpiled backend, the tests through the open-abap runtime - in an integrated terminal named "abap2UI5 unit tests", reused from run to run, so the log and the per-method summary arrive live. Invoked on a class (or its test include) only that class runs; from the palette the project'ssrc/does. Anmcp-servercheckout underabap2ui5.mcp.reposRootruns from disk with the same*_HOMEvariables the MCP registration passes, and anabap2UI5checkout there is handed over as the framework; without themnpxfetches the published package and the runner clones the pinned release into~/.abap2ui5-mcp- the terminal says so up front, because that first run takes minutes and every later one seconds. The settings that choose the program are the MCP server's restricted ones, and in Restricted Mode the command does not run at all: the tests are the repository's code. -
"New Project from Template" hands out app-template's named files too. The starter class, its abapGit sidecar and the new ABAP Unit test include (
src/<class>.clas.testclasses.abap, a test double forz2ui5_if_clientdrivingmain( )),.abapgit.xmlandpackage.devc.xmlcome out of the app-template snapshot and are renamed the way the template's owntemplate.jsondescribes - the sidecar carriesWITH_UNIT_TESTS, the include names the chosen class. The scaffold used to write those files itself and a project from the IDE had no test. Because that test asserts on the template's starter class, the project wizard no longer offers the gallery pick; "New App from Template" keeps it. -
A warning when the framework pin is older than the linter assumes. The bundled linter now states which abap2UI5 release its rules assume, and the extension compares the
"branch"of the abap2UI5 dependency in an openabaplint.jsoncagainst it: a pin below that minimum gets a warning on thebranchline naming the pin, the minimum and the remedy (bump the pin, runnpm run check), on open and on save. The abap2UI5 output channel says at activation which release line the bundled linter understands and which UI5 snapshot it carries ("Show MCP Status" repeats the line) - or that the bundled linter ships no such record yet, in which case nothing is compared. -
Quick fix for
event-without-handler: "Add a WHEN branch". The lightbulb on a raised event nothing handles now offers to write the handler branch into the class'sCASE client->get_event( )(orCASE client->get( )-event) - beforeWHEN OTHERS.when there is one, otherwise beforeENDCASE., indented like the neighbouring branches and in their quote and keyword case, with a" handle NAMEplaceholder line. The name is spelt the way the view raises it, becauseget_event( )compares letter for letter. A class without such a CASE gets no offer - where a handler belongs in an IF-shaped dispatcher would be a guess. -
New command "Generate Mock Data for This App" (
abap2ui5.generateMock, in the editor's abap2UI5 menu and the apps tree). It writes the<class>.mock.jsonthat Preview View (No System) and the linter's--screenshotread, from the model shape the linter derives out of the class: a readable sample per string,0,false, two example rows per table, nested structures filled recursively. It asks before replacing an existing file, opens the result beside the class, and names the roots typed outside the class (a DDIC structure) that it had to leave empty. For a class opened through ADT there is no "beside", so the skeleton opens as an untitled document to save into the workspace. -
New command "Apply the Model Document to the Running App" (
abap2ui5.applyModel, also in Preview Actions). The{ }button dumps the running app's model, the pin restores a captured model after a reload- this is the direction that was missing: on the dump it opens an editable copy; on that JSON it pushes the values into the running app through the same restore path the pin uses, so a table can be filled or a flag flipped without changing ABAP. Only the class's own model paths travel (matched case-blind, framework state and unknown keys are dropped and named), and the app's class has to be open in the editor for the paths to be known.
-
New snippet
z2ui5test: an ABAP Unit skeleton for the*.clas.testclasses.abapinclude - altd_clienttest double implementingz2ui5_if_clientpartially (answerscheck_on_init/check_on_navigated/check_on_event/get_eventfrom attributes, records the displayed view XML and every toast) and altcl_apptest class with one test that sets an event, runsmain( )and asserts the toast. Like every shipped snippet it is held to the bundled linter.
-
Other 44
Notes
A review round: every module read against the linter it ships and against the API contracts it relies on. Bug fixes throughout, no new commands.
-
The bundled property gate reports what the CLI reports, for three more rules.
absent-boolean-overrides-defaultnever fired in the editor: the gate handed the linter's ABAP rules the enum-typed fields of a bound table but not the boolean ones whose default istrue, so a row that omitsiconInsetwas red in CI and clean here. A class that builds a view and never displays it - or builds its views in helper methods the reconstruction cannot follow - answered "nothing to check" in the editor while CI reportedview-never-displayed, the flow rules and the ABAP hygiene rules for it; the ABAP-side rules now run over such a class too, and "nothing to check" is only said when there is indeed no finding. The parity test that holds the gate to the linter's own pipeline covers all three with the linter's own fixtures. -
The lightbulb offers every fix
abap2ui5lint --fixapplies. The did-you-mean corrections (a misspelt control, property, aggregation, enum value or event parameter) and the deletion of ajson = abap_trueon a scalar property were attached by the CLI only - the quick fix, "fix all", the Autofix code lens and "Fix Workspace" saw the finding but no fix, in ABAP and in XML views alike. The parity test now compares the fixes as well, so a fix cannot go missing on one side again. -
"Suppress on this line" in an XML view now works for an attribute inside a multi-line start tag. The directive was written above the tag's
<line, but the linter records the finding on the attribute's own line and reads directives per line - so the comment was placed correctly and suppressed nothing. Such a finding now gets anabap2ui5lint-disable <rule>/abap2ui5lint-enablepair around the whole tag, which the linter honours; a tag on one line keepsdisable-next-line. Both are proven against the bundled linter. -
"Check Workspace", "Fix Workspace" and "Update Baseline" look at exactly the files the CLI collects. The sweep used to take every
*.abapthat calls the builder - including*.clas.testclasses.abap,*.clas.locals_imp.abapand reports the CLI never looks at - and ignored the config'signorepatterns and dot-directories. A baseline rebuilt from the editor could therefore carry entries CI fails as stale. The sweep now follows the CLI's collection rule (*.clas.abapand view/fragment XML, nonode_modules, no dot-entries, the config'signorehonoured, directories included), and the baseline rebuild applies the same rule once more. -
On vscode.dev,
enum-value-too-newfires. The web build's snapshot loader attached the enum table and the UI5 version the way the linter does, but not the per-value@sincetable the rule reads; the hidden tables are now held to the linter's own loader by a test. -
On vscode.dev,
abap2ui5lint.jsoncis read the way the CLI reads it. The web build parsed the file as plain JSONC: an unknown key or rule id the CLI refuses was accepted,"distribution": "OpenUI5"was not recognised (which turnedsapui5-only-controlfrom an error into a hint) and a numericui5stayed a number. The linter's ownparseConfigvalidates and normalises it now. Anextendscannot be followed in the browser host and is said so in the output channel instead of being dropped silently. -
Workspace fixes land on the right character in files with a byte-order mark. The sweep read files from disk with the mark kept, the editor's document has none, so every fix in such a file was one character off. The sweep decodes like the live check does; as a consequence the sweep no longer reports
byte-order-markfor a file on disk - the live check never did. -
abap2ui5.viewCheck.distributioncan be left undecided. The setting used to default tosapui5, which silencedsapui5-only-controlin every workspace that never thought about the distribution - while a configlessabap2ui5lintrun reports the control as a hint. The default is now empty, meaning "not decided", and the gate answers as the linter does: a hint, and the cue to write the distribution intoabap2ui5lint.jsonc.sapui5andopenui5behave as before. -
A repository with
render: falseno longer hears "view check passed" for a render gate that did not run. The render gate was started regardless of the config, came back with an empty report and read as a pass; it is now not started, and the message says "render gate off by config". Arules['render-error']entry withexcludepatterns is applied against the real file's path (the CLI had matched it against the scratch copy in the temp directory, where it matched nothing), and its severity reaches the Problems panel. -
A misspelt severity in
abap2ui5.viewCheck.rulesno longer shows as an error."some-rule": "critical"is not a severity the linter knows; it used to render every such finding as Error and is shown as Information now. -
A change to a config named in an
extendschain is picked up at once. The repo config's cache watched the mtime of the discovered file only, so an edit to the base config it extends changed nothing in the editor until the extending file was touched; every file in the chain is watched now. -
Changing a setting or a config file re-checked every open document twice; once is enough.
-
The
{ }model button works for a namespace class./UI2/CL_APPstarts with a slash, the virtual document's path therefore started with two, and the URI validator refused it - the button threw into the host log and did visibly nothing. The path uses the file-safe stem now; the raw name stays in the header comment. -
Reloading and revealing an app tab no longer moves it.
reveal( )MOVES a shown panel to the column it is given, and "Beside" is relative to the active group - with three groups every F9 dragged the app tab from group 3 to group 2, and with the tab itself active it opened a fourth. An existing tab is revealed where it is; "Beside" is where a NEW tab opens. -
Focus returns to the source only when there is one. A start from Recent, the welcome view, the system search or MCP has no editor, and the previous F9's document came back into focus - reopened, even when it had been closed since. Such a start forgets the remembered source, and a closed document is never reopened.
-
The XML preview no longer freezes after a while. VS Code drops a hidden virtual document after some minutes and asks for it again when the tab comes back; the preview read that drop as "tab closed" and stopped following the class for good. The open tabs decide now, and being asked for the content means the preview is open.
-
A theme or language switch keeps the activation watch running. The preview reloads the version the server already has; nothing about the server changed, so a watch waiting behind the "not activated" badge keeps waiting instead of being cancelled.
-
No live token in the output channel, no logon parameters in tooltips. A runtime error UI5 reports quotes the page URL - proxy origin and capability token included - and it went into the channel users paste into issues verbatim; it is redacted like the generated report. The full launch URL in the status-bar tooltip and the toolbar tooltip is shown without
sap-user/sap-password. -
release.ymlaccepts an annotated tag. The retry check compared the tag OBJECT's hash with the commit and refused aworkflow_dispatchretry with "already released from commit"; it reads the peeled ref first. -
manifest.test.tschecks two more promises: everyviewItem == …clause names a contextValue the code sets, and every file the manifest points at - the icons, the walkthrough pages - exists. -
Removing an attribute in the Control Properties view no longer takes its neighbour with it. When two
a( )calls shared a line, removing the second one cut the whole line - the first attribute included, and in the last line of a chain everything but the closing).. The edit now cuts exactly the call it was asked to remove, and keeps the chain balanced. -
"Add attribute" works on the last control of a chain. Its block ends with
… ).on the attribute's own line, which the property editor read as "not chain style" and refused - on the final control of every chain, in all five templates. The new line now goes in front of that)., in the chain's own layout. A value continued over&&lines keeps its continuation lines together. -
Renaming a bound attribute writes the binding path the framework derives. abap2UI5 addresses
DATA mv_titleas{/MV_TITLE}- the name upper-cased - and F2 wrote the new name into the path as typed:{/mv_header}, a path that binds to nothing, which is the silently empty wire the rename exists to prevent. The identifier is written as typed, the path segment upper-cased. -
F2 refuses a field of a
DATA: BEGIN OF … END OFstructure. Its binding path is nested ({/MS_DATA/TITLE}), which the rename deliberately leaves alone - so accepting the field renamed its declaration, every same-namedTYPESfield andls_other-title, and left the wire behind. The structure itself stays renameable, path included. -
An abbreviation expanded inside a chain closes the containers it opens.
Panel>content>Buttonexpanded mid-chain used to leavePanelandcontentopen, so the line below the cursor - written for the Page - became a child ofcontent. The expansion now ends with theend( )calls the next line's leading)completes. -
F12 on
client->view_display( )no longer jumps to the class's ownview_display. Go to Definition looked at the method name alone, and every template declares a method of that name. A call on any receiver other thanme->is left to the ABAP extension. -
The navigation map follows
CAST z2ui5_if_app( NEW zcl_x( ) )tozcl_x. The first Z-name in the call - the interface the result is cast to - used to be drawn as the target. -
BEGIN OF ENUMandBEGIN OF MESHdeclare their type, not a type calledENUM- which is what the roundtrip-cost annotation and F2 read. -
A class shown in a diff is not a second class. The revision side of a
git:diff (and GitLens', the pull-request extensions' and output channels' read-only views) has the ABAP language id too, and entered the source scan as a duplicate: a second row in the apps tree, and an old revision that could shadow the class's inheritance entry. Those schemes are excluded; ADT's and every other virtual scheme keep working. -
Converting XML reports an unquoted attribute.
enabled=trueis not XML and was dropped without a word; the conversion now says so, like it does for text content and mismatched tags. -
Colour swatches follow the builder's ownership rule. An attribute written after
end( )belongs to the container just closed - the rule completion and the view check already applied - not to the last control written above it, so a container's colour property gets its swatch even when the container has children. -
Extract to View Method accepts a name starting with
_, as ABAP does (the framework's own_bindand_eventstart that way); the input box and the refusal share one rule instead of two copies. -
The property editor never reads a value across a line break (an ABAP literal ends at the end of its line), and a
CLASS … DEFINITIONwritten inside a multi-line string template no longer names the class for the CodeLens, F9 and the inheritance lookup. -
A system with an IPv6 address works. A launch URL such as
https://[fd00::10]:44300/...failed every request with "the hostname does not resolve (DNS)": the brackets of the address travelled into the name lookup. The preview, the ADT lookups and WebSocket upgrades now connect to the address itself. -
A launch URL typed without its scheme is refused where it is typed.
host:44300/sap/bc/z2ui5?app_start={class}parses as a URL - with the schemehost:- so it was accepted, the credentials were stored under the origin "null" ("SAP User for null"), and the launch then failed with "Invalid URL". A template has to be http or https now. -
A wrong password no longer lets a WebSocket-using app hammer the system. An app that reopens its WebSocket after the system refused the logon sent one failed logon per attempt - the burst that locks an account - and the offer to re-enter the credentials never came, because the refusal was not reported. The upgrade path now trips the same breaker as every other request and reports the 401.
-
WebSocket tunnels are cleaned up when the app gives up first. A client that hung up before the system answered left both ends of the pending upgrade open until the proxy stopped.
-
The proxy is stricter about what it forwards and who may ask. An absolute-form request (
GET http://127.0.0.1:<port>/...) used to be forwarded to the system verbatim, token segment included; it is refused. A request authorized only by the proxy's cookie is accepted only from the page the cookie was planted for - a cross-site<img>, iframe or form on another local port is refused, told apart by the browser's ownSec-Fetch-Site. And theRefererof a page loaded through a one-shot screenshot URL is rewritten to the system's origin like every other, instead of carrying the one-shot token on to the system. -
System cookies scoped to a path work through the proxy. A cookie the system set with
Path=/sap/bc/z2ui5was never sent back, because the proxied requests live under/__abap2ui5/<token>/...; the path is rewritten to/, on pages and on WebSocket handshakes alike. A root-relative redirect (Location: /sap/bc/...) now lands behind the token too, rather than on the cookie alone. -
The system MCP server ignores browsers. A request carrying an
Originheader - a page made it, not an MCP client - is answered 404, and a POST whose body is not declaredapplication/jsongets 415, so a cross-site "simple" POST can no longer reach a tool. -
Screenshots: unique file names, no credentials on the command line. Two shots within one second no longer overwrite each other (the name carries milliseconds and a counter), and a launch URL carrying
sap-userorsap-passwordhas them stripped before the page URL is handed to Chromium- the proxy injects the credentials anyway.
-
Class descriptions from the system show
&,<and quotes correctly in "Run an App from the System"; they arrived entity-escaped (&) and were shown that way. -
A pasted SAP user is trimmed before it is stored, so a trailing space cannot turn every logon into a failure until the credentials are reset.
-
"Preview View (No System)" survives an empty
viewCheck.command. Set to"", the checker start threw instead of reporting that nothing could be started; it is reported like any other failed start now, and ataskkillthat cannot start on Windows no longer surfaces as an uncaught error.
-
Other 3
Notes
Small fixes, and the linter's newest rules.
- A new project's
npm testruns again. app-template grew acheck:allscript that chains through itscheck:pin— a script that reads a file out of the template's ownscripts/directory, which a scaffolded project does not get and which "New Project from Template" therefore does not write. The scaffold copied the chain verbatim, so a new project shipped two scripts (check:alland thetestthat calls it) that existed and died with "missing script". It now takes the links to a dropped script out of the commands that survive, and drops a command that was nothing else. - The bundled view check runs the linter's current rules — thirteen more
than the last release. Among them the flow rules that read a method's
statement sequence (a popup rebuilt on every roundtrip, a display behind a
nav_app_call, the same slot displayed twice), an event raised in a spelling its handler does not have, a library SAP deprecated whole, aDELETEinside theLOOPthat reads the table, and the abapGit round-trip family (BOM, CRLF, trailing whitespace, a missing final newline). Eight closed-set rules now name the one candidate a misspelling can only have meant and offer a Quick Fix that writes it. client->hover and completion match the framework again: the bundledz2ui5_if_clientreference is back in sync with the interface, including the documentationmessage_box_displaygained.
- A new project's
Other 8
Notes
More of the extension in the browser, and a faster editor everywhere.
- Three more features work on vscode.dev, github.dev and browser-based Business Application Studio: the App Navigation Map, the Control Properties view and the abap2UI5 Findings tree now run in the web host too. The findings tree fills from the web view check; only "Add to the Baseline" stays desktop-only (a baseline is a file on disk) and says so instead of failing.
- Less work per keystroke. The view check and the Autofix CodeLens now reuse the reconstruction the language features already computed, instead of parsing the class a second time on every pause and every lens pass.
- "Check All Views in the Workspace" is faster on large repositories: the file reads run concurrently ahead of the checks, and the progress notification updates in batches instead of per file. Results and their order are unchanged.
- Lower memory footprint in big workspaces: the app-class index (the one
behind F9 and the apps tree recognising inherited
z2ui5_if_app) keeps two facts per class instead of every class's full source, and the inline roundtrip-cost annotation bounds its mock-file cache. - Faster window startup: the extension no longer activates in every
window (
onStartupFinished). It activates when ABAP or view XML is around — and for MCP clients VS Code activates it on demand through the MCP provider contribution, so agents see the abap2UI5 servers exactly as before. - Cleaner packaging: the production build now cleans its output
directories first, so a locally built
.vsixcan no longer ship stale files left behind by earlier dev or test builds. - A settings reference in the README, generated from the manifest so it cannot drift, and an accurate description of the test suite.
- For the ecosystem: the weekly bump workflows can now be fired by the
upstream repositories on merge (
repository_dispatch), so a new linter rule or template change can reach the editor the same day.
Other 11
Notes
An audit pass: five reviews over the whole extension produced a list of defects, and this closes it. Most of these you will notice as things that stop going wrong.
- Credentials stay out of the logs. A launch URL carrying
sap-userorsap-passwordused to reach the "abap2UI5 Traffic" channel — and the traffic an MCP agent can read — in full. Both are redacted now. Screenshots authorize with a token that is retired the moment it is used, instead of one that sat in the browser's command line for the life of the process. - Reload on activation survives a wrong password. A single 401 used to switch the activation watch off for the rest of the window, silently: you re-entered your credentials, everything else recovered, and the preview simply never reloaded again until you reopened the window.
- F9 and the CodeLens follow the branch you switch to. Classes that
arrive through
git pullor a branch switch are picked up now, so an app that inherits itsz2ui5_if_appfrom a base class stops going quiet after a checkout. - The view check no longer overstates itself. "View check passed" is only
said when the render gate actually reported; a timeout, a crash or an
unreadable report says so. Installing the render gate after a failed start
works without reloading the window, and pointing
mcp.reposRootat a local linter checkout is noticed the same way the other settings are. - Findings stay honest in the background. Turning a rule off, rebuilding
the baseline or pulling a changed
abap2ui5lint.jsoncnow re-checks every open file, not just the visible ones, so a background tab cannot keep showing findings the repository has waived. On vscode.dev the live check and the workspace sweep finally agree on which files a config excludes, and editing while a workspace check runs no longer wipes the squiggles it just produced. - The editor keeps up while you type. The ABAP scan behind completion, hover, CodeLens, colours and the inline annotations runs once per edit instead of six to eight times, and the apps tree stops re-reading the workspace every time you save or click one of its entries. Large classes and large workspaces are where you will feel it.
- Two new ways to follow a wire. Control ids complete inside
set_focus( ),by_id =andcontrol_by_id( )— the literals where a typo used to fail silently — and putting the cursor on an id or an event name highlights its other end. - Renaming an attribute rejects a name that would not compile.
F2used to acceptmv-titleand rewrite the declaration and every use into ABAP the system refuses. - The property editor edits the control you are looking at. An edit sent while the form was moving to another control could land on the wrong one.
- The wizards say what went wrong. "New App from Template" no longer ends in silence in a read-only editor, and a failed "New Project from Template" names the file it could not write and how much it had created, instead of leaving a half-written folder that the next attempt refuses.
- Turning the MCP system server off really stops it, including clients that were already connected, and a malformed request from one MCP client no longer takes the rest of a batch down with it.
- Credentials stay out of the logs. A launch URL carrying
Other 7
Notes
A dedicated polish pass: 138 small improvements, none of them a new subsystem, all of them the kind of detail you notice daily.
- Messages act, not just inform. "Show Log", "Show Problems", "Open
Baseline", "Open abap2ui5lint.jsonc" and "Set Launch URL" buttons appear on
the messages that used to just point somewhere; the invalid-launch-URL
message quotes the offending template; the screenshot toast names the file;
proper pluralization everywhere ("1 finding", "3 findings" - never
"1 fix(es)"); button labels, picker titles and command titles follow one
consistent style, and every command now carries the
abap2UI5category instead of a hand-written title prefix. - The preview remembers more. The device width survives closing the
preview, "Save As…" starts in the folder you last picked, screenshots are
pruned to the newest 20 instead of accumulating forever, and additional
logon languages can be added to the language picker
(
abap2ui5.previewLanguages, the counterpart ofpreviewThemes). - Waiting has an exit. The connection check runs as a cancellable notification with a 30-second probe deadline (a cold ICF service is no longer misdiagnosed as a network problem), the render-gate install can be cancelled during the Chromium download and no longer freezes the window while deleting an old install, and a failed system search says so in the picker instead of looking like "no matches".
- Completion got opinionated. Deprecated controls and members sort below
their living siblings, enum values keep their declaration order (Default
first) and show their type, WHEN completion shows how often each event is
raised, typing
(accepts the highlighted client method and opens signature help, andlo_http_client->no longer gets abap2UI5 hover - only the realclient->does. Signature help works on continuation lines and highlights the right parameter even when names overlap. - Findings read better. All obsolete-API rules render strikethrough,
"does nothing" findings render faded, quick-fix titles name what they
change ("fix obsolete-binder on _bind_edit"), the findings tree shows
per-finding severity icons and opens at the exact column, and the web
check sweeps
fragment.xmlfiles, names the file in its progress and says when it hit its file cap. - The tools speak up. The wizard preselects the name part of
zcl_my_app, names every conflicting file and the exact invalid character, and opens the starter class instead of reloading the window; converting aDialogemitspopup_display; the navigation map has hover tooltips, a Refresh button and a truncation note; the MCP tools return count/summary lines an agent can branch on; a new Popup-Close snippet completes the popup round trip. - Housekeeping: CI cancels superseded runs and every workflow has a
timeout; the generator scripts fail fast on stalled fetches;
test:grepruns one slice of the suite; the walkthrough no longer references a 🎯 button that doesn't exist; log redaction also covers Authorization headers.
- Messages act, not just inform. "Show Log", "Show Problems", "Open
Baseline", "Open abap2ui5lint.jsonc" and "Set Launch URL" buttons appear on
the messages that used to just point somewhere; the invalid-launch-URL
message quotes the offending template; the screenshot toast names the file;
proper pluralization everywhere ("1 finding", "3 findings" - never
"1 fix(es)"); button labels, picker titles and command titles follow one
consistent style, and every command now carries the
Other 35
Notes
The result of a full adversarial review of the extension: over sixty bugs fixed, the per-keystroke hot paths measured and cut down, and a round of features that the existing plumbing had almost finished anyway. The highlights, by surface:
-
The preview's badges now act. The "not activated" badge offers Activate directly (reloading only showed the old version - the exact trap the badge warns about), the runtime-error badge jumps to the source line it already knew about (it used to only write it to the log), and when the activation watch gives up after ten minutes it now says so in the preview instead of leaving the badge promising a reload that will never come. The welcome screen offers to relaunch the last app after a window reload, the screenshot button captures at the preview's current device width (desktop/tablet/phone), and the status-bar entry opens a small menu - Reload, move the preview, open in the browser, screenshot, connection check - instead of only reloading. Custom UI5 themes can be added to the theme picker via the new
abap2ui5.previewThemessetting. -
Two previews could end up running the same app. Changing
abap2ui5.openModein the settings (rather than through "Move Preview") and pressing F9 left the old surface alive: every save reloaded the app twice, and closing the leftover tab killed the status bar for the panel that was still showing the app. F9 now reconciles the surfaces the way the move command always did. A launch canceled at the credentials prompt no longer lands in "Recently launched". -
The render gate installs reliably again. The rolling bundle (
viewCheck.rollingBundle) was uninstallable from the second time on - the "content must never change" integrity check applied to the one URL whose content changes by design. A stalled download no longer disables installing for the rest of the session (it times out, can be cancelled, and says what happened), and the new "Update Render Gate" command shows the installed digest next to the pinned linter commit before refreshing. -
The proxy fails closed, and two windows stop fighting. During the short window while the proxy shuts down, a request without the token cookie was accepted instead of refused (nothing was forwarded - the credentials were already gone - but the gate is now checked first and fails closed, and both the path token and the cookie are compared timing-safe). The auth cookie is now scoped per proxy port, so two VS Code windows pointed at two systems no longer overwrite each other's cookie and blank each other's preview. Stopping or switching systems no longer waits up to two minutes for a hanging request, a failed port bind no longer leaves a half-started proxy that hands out
port: undefined, and WebSocket connections are now forwarded - an app using them found the tunnel dead before. -
Correct credentials could get locked out. The background UI5-version probe ran without the launch URL's
sap-client; on systems whose default client rejects the logon it tripped the proxy's 401 breaker with perfectly valid credentials - breaking the running app, prompting for a re-entry, and counting a real failed logon toward the system's lockout, once per F9. The probe now carries the client and can no longer trip the breaker. "Check System Connection" gained a client-mismatch probe that diagnoses exactly this setup, and its TLS classification now recognises the private-CA error codes (UNABLE_TO_GET_ISSUER_CERT*). Credentials migrated from pre-0.14 installs can be deleted again by "Clear Stored SAP Credentials", and adding a system under an already-used name no longer activates the old one. -
A cancelled check no longer leaks a browser. On macOS and Linux, killing a running checker killed only the wrapper process - the linter and its Chromium kept running (the Windows path always killed the tree). The whole process group dies now, a closed preview panel kills its render instead of letting Chromium finish for nobody, and checker output with umlauts can no longer be corrupted at a chunk boundary.
-
The editor and CI disagree less. Classes on a frozen view builder are reported as
frozen-view-builderin the editor (they were "nothing to check" while CI failed them),rules.*.excludepatterns anchored to the repository root now match in the editor too, the suppress quick fix places its directive on the right line in CRLF files (it could land inside a start tag and break the XML), diagnostics are cleared when a file stops being checkable, workspace-check results no longer land on text that changed during the sweep, and rebuilding the baseline in a repository with nested configs no longer writes entries CI would immediately call stale. A changed baseline file (agit pull) now triggers a re-check on desktop, and the web build reacts to settings changes without waiting for a keystroke. -
Baselines and fixes with more control. "Fix All in Workspace" and "Extract View" open VS Code's refactor preview instead of applying blind, a rule group in the findings view can be baselined as one action, the suppress quick fix has a "with a reason" variant, the findings status-bar item opens the abap2UI5 findings view rather than the generic Problems panel - and "Check All Views in Workspace" now works on vscode.dev and Business Application Studio too.
-
Apps are recognised the way teams actually write them.
INTERFACES: if_something, z2ui5_if_app.- the interface anywhere in a chained declaration - is an app now (only the first position was), and a leadingCLASS ... DEFINITION DEFERRED.no longer hides a class's superclass, which silently disabled the inheritance lookup for exactly the base-class pattern it exists for. -
Completion and hover answer for the right control. An attribute written after
)->end( )belongs to the closed container - the builder and the linter always knew, completion and hover now agree instead of offering the inner control's members and then squiggling the accepted offer.client->hover and completion stay quiet in comments and string literals, go-to-definition on a binding path no longer lands on a commented-out declaration,DATA: BEGIN OF ...blocks no longer annotateBEGINandENDas roundtrip cost, and a>inside a quoted XML attribute no longer kills completion for the rest of the tag. -
New language smarts: signature help for
client->methods, completion insideWHEN '...'offering the events the view actually raises, a warning lens on an_event( )noWHENbranch handles, deprecation as an inline annotation (toggle:abap2ui5.inlineDeprecated), completion forxmlnslibrary values, and the default aggregation marked in member completion. Events raised with a nestedVALUE #( )before the name are now found by F2, go-to-definition and the lenses, andWHEN 'A' OR 'B'branches count all their alternatives. -
F2 on an attribute stays inside its wires. Renaming
titleno longer rewrites a same-named component inside{/MT_TAB/TITLE}paths orTYPESblocks (and structure components, whose relative bindings F2 cannot track, are honestly not offered for rename any more). Inspect mode finds the builder call for a control inside an aggregation template instead of the first control in the document, and "Extract View" no longer writes a broken class when methods are declared in a chain or the file holds two classes. -
Converting XML views got sharper. Event handlers become
client->_event( \ON_PRESS` )wires with a TODO instead of dead string literals, lines longer than ABAP's 255-character limit are split with&&(they failed the abapGit import before), entity text like&survives the abbreviation round-trip, and Emmet abbreviations learned groups and counters ((Label+Input)*3,Button$*3). The XML preview follows the cursor (the ABAP side highlights the matching XML line), maps findings to the opening tag instead of sometimes the closing one, and the navigation map draws no arrows for commented-outnav_app_call`s - and its arrows are clickable now, jumping to the call. -
The example search tells the truth. Commented-out chains no longer count as (or rank) examples, the three remote catalogues are fetched in parallel instead of one after another, the corpus walk no longer freezes the extension host, a remote hit opens read-only in the editor instead of bouncing to github.com, and the day cache revalidates with ETags.
-
The MCP surface grew and locked down. New tools:
check_view_source(the bundled view check over agent-supplied source),get_traffic(the proxy's recent roundtrip log), and aviewportparameter plussap-ui-themesupport forrun_app_on_system. Turningabap2ui5.mcp.systemoff now actually stops the running server (a connected client kept its access before), its token is compared timing-safe, the servers are registered even in a window without ABAP files, and the new "Show MCP Status" command says what the registration resolved. Screenshots work for namespaced classes (/UI2/CL_...) and the file name can no longer be steered outside the storage folder. -
Hardening and housekeeping: releases run the test suite before publishing,
abap2ui5.systemsandabap2ui5.launchUrlTemplateare restricted in untrusted workspaces (a cloned repository could point F9 - and the credential prompt - at its own host), the apps tree notices files changed by agit pull, the.vsixno longer shipspackage-lock.json, and the per-keystroke paths (live check, code lens, rename, inline annotations, completion docs) each do their expensive work once per document version instead of once per consumer. -
The icon rules now judge
.view.xmlfiles too. An unknown or too-newsap-icon://name was reported by CI and not by the editor whenever it sat in a view or fragment XML file rather than in an ABAP class - the icon scan was not reachable from the in-process gate. It is now, so both paths judge the same file the same way. -
Fewer false positives from the view check. A relative binding path under a slot the class element-binds (
cs_event-bind_element) has a context at runtime that no static analysis can see. The linter knows that and accepts the path; the editor did not and squiggled it. It now asks the same question, per view document rather than per class. -
Two more rules reach the editor: the picker whose bound value has no format, and the enum-typed row field left unset on an insert - the second is a runtime error in UI5's strict validation, which takes the binding update and the view down with it.
-
F9 recognises an app that INHERITS
z2ui5_if_app. A shared base class carrying the interface and the lifecycle methods, with each app redefining them, is a common way to keep a team's apps uniform - and the extension recognised none of those apps: it read the interface out of the class in front of it, so F9, the "Run"/"Activate & reload" CodeLens, the abap2UI5 Apps tree and the navigation map all stayed silent on the very classes a developer works in every day. The inheritance chain is now followed, over as many levels as it has, using the classes the window can see (the workspace's files plus everything open, so it works against ADT too). A base class this window cannot see still means "not an app" - the same answer as before, rather than a guess. (#81) -
The view check works again in the browser. On vscode.dev and browser- based BAS every check threw before it produced anything, and the error was swallowed by design, so the whole feature was silently dead: no squiggles, no Problems entries, no completion metadata - with nothing anywhere saying why.
-
The editor reports the findings CI reports. Five rules never fired in the editor although the linter reports them on the same file:
unknown-model,json-bind-on-scalar-property,raw-javascript-to-frontend, and the three structural ones that assert at RUNTIME -excess-shut,duplicate-propertyandattribute-without-element. The icon rules (unknown-icon,icon-too-new,icon-removed) had never fired in the editor at all, and the configured minimum UI5 version now reaches them, so a repository with a higher floor is judged against its own floor. -
"Fix All Views in the Workspace" no longer corrupts unsaved files. A class that was open with unsaved changes AND on disk was checked against the version on DISK, and the resulting fixes were then applied to the buffer - so every fix after the first difference landed a few characters off, in the middle of a token. Open files are now checked as they stand, and a file edited while the sweep runs is skipped and reported instead of edited blind. The same mismatch had put the workspace check's squiggles on the wrong lines.
-
Format Document agrees with the linter. It worked the chain layout out for itself and was measurably stricter than
chain-house-layout: eight of the 637 builder classes in samples-controls would have been re-indented although the rule calls them correct. It now applies the linter's own layout fixes, so formatting and CI cannot disagree. -
Redirects from the system reach the preview. A
Locationheader that spelled the host in another case, or its port differently, was not rewritten onto the local proxy - the browser then followed it to the system directly, without the injected credentials, and the preview stayed white. -
Reload-on-activation survives a system switch. One launch against a system whose
/sap/bc/adtis closed used to switch the feature off for every other system too, until the window was reloaded. -
Fewer wrong answers from the language features. A commented-out argument inside a builder call decided which control completion, hover and the property editor were talking about; a doubled quote (
`its fine``) truncated a value, and rewriting it from the property editor corrupted the literal; a commented-outWHENbranch was where Go-to-Definition landed and what F2 renamed;control_by_idmentioned in a comment made the next literal look like a control id. String templates with embedded expressions (|val { get( 'a|b' ) }|`) no longer swallow the rest of the line. -
"Show Examples for this Control" finds most of the corpus. It matched only
tag( n = \Button` )and not the positionaltag( `Button` )`, which is what the XML converter emits and most of what the sample catalogues contain - so common sap.m controls returned no local hits at all. Hits in samples-stack now link to the branch their file is actually on instead of 404ing. -
The view preview and the screenshot cannot hang the panel any more. Both run the checker with a timeout and kill the whole process tree, as the view check already did; a Windows path with a space in it (
C:\Users\John Smith\...) no longer breaks either of them, nor aviewCheck.commandpointing atC:\Program Files\nodejs\node.exe. -
The render gate verifies what it downloads before extracting or running it, and the extension's second local listener (the System MCP server) now refuses a non-loopback
Hostexactly as the auth proxy does. -
The preview's toolbar cannot be spoofed by the app it shows. The embedded app could post the host's own messages and rewrite the class name, the system URL shown under it and the reload target.
-
Desktop-only commands are hidden in the browser. The editor-title play button and F9 appeared on vscode.dev, where those commands do not exist, and answered "command not found".
-
The
client->hover links to the published Client API Reference. The hover shows a method's signature and ABAP Doc, but it was a dead end: no way to read on - the parameter tables, the constants, the neighbouring methods of the same group. Every hover now ends with a "Client API Reference" link to the generated page on the documentation site, deep-linked straight to the method's own heading (client->view_displayopens at#view-display), so the full reference is one click from the code. -
"Show Examples for this Control" works without a single clone. The command used to read only the local checkouts under
abap2ui5.mcp.reposRoot- and the beginner most in need of an example is exactly the person who has cloned nothing yet, so they got an empty result. Each sample repository without a local checkout is now answered from thecatalogue.jsonit commits at its root, fetched from GitHub and cached for a day: those hits name whole samples - the ports that demonstrate the control first - and open on github.com at the class. A local checkout stays the primary source and always wins for its repository (it is searched line by line and opens in the editor); a failed fetch quietly falls back to the previous behavior. Only when there is no checkout AND no fetched catalogue does the command say so, offering to open the setting or retry the download. -
"Check System Connection" diagnoses a white preview. The most common first-run failure is a launch URL that is slightly wrong, and its symptom is the least helpful one possible: an empty white frame. The new command probes the configured system the way F9 would - the same URL expansion, the same stored credentials, the same proxy plumbing - and reports step by step where a launch ends: the URL's shape (
{class}placeholder, scheme), the host (DNS, port, TLS certificate, VPN), the HTTP status (401/403 with the system's own words and whether basic auth is offered at all, 404 with the SICF hint and the path that failed), and whether a 200 actually is an abap2UI5 bootstrap page rather than a logon form or something else entirely. Each failing step names its fix; the full stepwise report lands in the abap2UI5 output log. The preview's "taking longer than usual" overlay and the getting-started walkthrough offer the check right where the wall is usually hit.
-
Other 2
Notes
-
A launch URL without a path loads past its first request. (#60) The preview swaps the system's origin for the local proxy's
http://127.0.0.1:<port>/__abap2ui5/<token>prefix. Forhttps://host/sap/bc/z2ui5?app_start=Xthat leaves the token as a directory; forhttps://host?app_start=Xit left the token as the LAST PATH SEGMENT - which a browser drops when resolving every relative url on the page. The UI5 bootstrap and every module were then asked for as/__abap2ui5/<resource>, token gone, nothing loaded - a white preview whose only trace on the system was the initial GET, with the roundtrip POST never arriving. The frame URL is now rebuilt from the parsed launch URL (pathnameis never empty), so the token is always followed by/and survives resolution.Rebuilding instead of substring-replacing also fixes a quieter miss: a launch URL that never spells its normalised origin verbatim (an uppercase host, an explicit
:443) leftreplace(origin, proxyOrigin)a no-op, and the iframe loaded the system directly - without the credentials only the proxy can inject. -
The proxy's own cookie now travels from the framed page. The token cookie that authorizes absolute-path requests (
/sap/public/...) was plantedSameSite=Lax- but the app page sits in an iframe whose top-level document is thevscode-webview://origin, so every request it makes counts as cross-site and a Lax cookie stays home. It isSameSite=None; Securenow, the same pair the SAP session cookies are already preserved with, and loopback is a trustworthy origin so Chromium accepts it over plain http.
-
Other 1
Notes
-
The preview is clickable. Twice now this was answered as a handshake problem, and twice the app kept eating every click. It is not a handshake problem.
sap/ui/security/FrameOptionsblocks the document the moment it finds itself framed - a capture-phase listener that callspreventDefault( )on every input event - and lifts that block only from_applyState(bRunnable, bParentUnlocked), which needs BOTH flags. The preview can supplybParentUnlockedand neverbRunnable:bRunnableneeds a same-origin parent, or an allowlist that matches the parent's origin. Ours isvscode-webview://<id>- not the system's, and in nobody's allowlist.- the first message from the parent runs
_check( )before the "parent-unlocked" branch, and_check( )without an allowlist ends in_callback(false).
So 0.24.1's answer was correct on the wire and still could not win. The proxy now serves the bootstrap with
sap-ui-frameOptions="allow"instead, which is defensible exactly here: frame protection stops a FOREIGN page from framing an SAP app to harvest clicks, and the framing page is this extension, loading a url it built itself over loopback behind a capability token.
-
Other 2
Notes
- A rejected logon stops at the first request, and says what the system said. One wrong password used to become one failed logon per resource: the proxy puts the same credentials on every request a UI5 page makes, so a single F9 was observed sending eleven 401s within seconds - two of them in the same second. A system with a lockout policy counts every one of them, so a typo could cost the user their account. The proxy now stops asking after the first 401 and answers the remaining requests itself, until credentials are entered again (which every F9 does). The ADT lookups, which run on a timer rather than on a keystroke, stop with it.
- "HTTP 401" alone was never a diagnosis. It fits a wrong password, a
locked user, an expired one, a client that refuses logons, and a system that
does not offer basic auth at all - and the extension had nothing else to
offer. The rejection now carries the
WWW-Authenticateheader (a 401 without one is not asking for a password, and no amount of retyping helps) and the system's own sentence, taken from the rejection page and redacted, both in the log and in the message that offers to re-enter the credentials.
Other 2
Notes
-
The render gate works against a system again. 0.24.0 taught the view check to see documents opened through ADT, and then started its checker in the workspace folder of whatever it was checking. For an ADT document that folder is a
repotree-v1route, not a directory - andspawnanswersENOENTfor a working directory that is not there exactly as it does for a missing executable, naming the COMMAND in both messages. So it read as "node could not be started", and the remedy offered was to install the render gate: 275 MB, after which the next check failed the same way and offered again. Four rounds is a gigabyte and no working gate.The preview never had this - it runs in a scratch directory - which is why the same class photographed fine while the check refused.
committedTextalready guards the same way; this was the one call site that did not. -
F9 apps react to clicks - this time against the real protocol. 0.24.0 claimed this and did not deliver it: the answer it posted was an invented JSON envelope with a
sentinelfield, andsap/ui/security/FrameOptionsnever sends or reads such a thing. Its wire format is two plain strings, and it drops everything else before looking:if (oSource === FrameOptions.__self || oSource == null || typeof sData !== "string" || sData.indexOf("SAPFrameProtection*") === -1) { return; }So the app asked with
SAPFrameProtection*require-origin, the preview replied with an object nobody parsed, and after ten seconds UI5 logged "waiting for a response from parent window" and blocked the frame - the app rendered and then ignored every click, exactly as before. It answersSAPFrameProtection*parent-unlockednow, which is the reply that unlocks (_applyState(false, true)) rather thanparent-origin, which only proves the parent is alive.
-
Other 13
Notes
-
The scaffold hands out the MCP server's new name.
abap2UI5/ai-mcpisabap2UI5/mcp-server, andsrc/data/app-template.json- the snapshot a new project is written from - still carried the old one, so every project created since the rename got anAGENTS.mdpointing at a repository that only exists through GitHub's redirect. Regenerated; the same run picked up two script entries the template had added in the meantime. Nothing in CI compares this snapshot to app-template'smainon purpose (this repository's build must not go red when another repository merges), which is exactly the window that let it drift - soAGENTS.mdnow says that out loud instead of claiming the weekly bump is a check. The remainingai-mcpstrings are the deliberate ones: the directory aliases every existing checkout is still found under, and this file's own history. -
The MCP server comes from npm now. The fallback when there is no local checkout was
npx --yes github:abap2UI5/mcp-server: a clone of whatevermainheld that minute, plus its install, every cold start. It isnpx --yes @abap2ui5/mcp-serversince that package exists - an immutable tarball of a tested release, and a much shorter first start. Left unpinned on purpose: the server's compatibility is with the corpora it reads, not with this extension, so a version fixed here would invent a coupling and then need an extension release behind every server release. -
npm run check. The four commandsAGENTS.mdhas always asked for before pushing, under the name every repository in the ecosystem uses for them. -
One command that writes the bug report for you. abap2UI5: Copy Diagnostics for a Bug Report collects what a diagnosis actually needs and puts it in your clipboard and in an editor tab: every open ABAP document with its uri scheme, whether the check considers it checkable, whether it builds a view, which config governs it and how many findings it carries - plus the versions, the ABAP extensions installed alongside, the settings you changed, the render gate's state, and the last 400 log lines.
The scheme is the point. A window can hold a checked-out repository and classes opened straight from a system through ADT at the same time, and almost every difference in behaviour between the two comes down to which of them a document is - invisible in a screenshot, and until now invisible in the log too. The check's own log lines carry it now, along with the config that governed each one.
Hostnames, credentials in urls,
sap-userstyle parameters and the proxy's own token are removed before you see it; the report opens in a tab so you can read it before pasting rather than taking that on trust. Ports survive, since "which port did it even try" is usually part of the answer. -
The system server's
run_appis nowrun_app_on_system. Both MCP servers this extension registers live in one window, and the abap2UI5 server (ai-mcp) already had arun_app— a build of the transpiled sandbox, no system involved. This one runs a class on a real system through the auth proxy. One name for the two of them left the agent guessing which it was calling; the name now says. -
Apps you launch with F9 react to clicks again. A system configured with
sap-ui-frameOptions=trustedlets its app run in an iframe only after asking the embedding window for permission. Nothing answered, so UI5 waited ten seconds and then blocked the frame: the app rendered and then ignored every click, with one line in the console to say why. The preview is the window that embedded it — it answers now. -
The systemless preview can take a picture. Preview View (No System) shipped against a checker option that no published bundle contained, so the render gate installed perfectly and then refused the only thing it was installed for. Reinstalling fetched the same bundle again, which is why the advice to do so was a loop; the message says what actually helps, and the pinned checker is now one that knows the option.
-
Working straight against a system, without a checked-out repository. Six surfaces asked what files exist, which answers nothing when a class is a document opened through ADT rather than a file on disk: the app list and the findings tree stayed empty, the navigation map drew nothing, Check All Views reported "no checkable files", Ctrl+T found no methods, and the preview never found its mock data. All of them now see the open documents too — and the findings tree had been throwing away findings the check was already producing. What cannot be enumerated that way is said out loud rather than reported as an empty workspace.
-
Which view-check rules are active is now a setting. A repository's
abap2ui5lint.jsonccould always switch a rule off or move its severity, but that file is the project's shared answer and needs one to exist.abap2ui5.viewCheck.rulessays the same thing per person and per workspace: a rule id maps tofalse, to a severity, or to{ severity, exclude }."abap2ui5.viewCheck.rules": { "unknown-binding-path": false, "control-too-new": "warning" }The repository still wins for every rule it names — editor and CI disagreeing about the same file is the one thing this must not cause — and the setting decides the rest. Since nobody knows rule ids by heart, the lightbulb on a finding now offers turn off in this workspace, which writes the id for you and offers to undo it.
-
Right-click has an abap2UI5 menu in ABAP classes and view XML: run and activate, the three ways to look at a view, check and fix, and the three editing actions — one entry rather than ten loose ones.
-
Fixes to what the reconstructed XML view points at. Switching to another class made the preview follow it before the re-render landed, and a jump from an XML line during that window used the previous class's offsets against the new source - landing somewhere arbitrary in it. It refuses instead, until the render it belongs to exists. And findings that cannot be recomputed on a half-typed buffer are now cleared rather than left pinned to lines the re-render has already moved.
-
Two things that failed silently now say so. Picking an app from the system search and having nothing happen was a possible outcome: starting it talks to a system, and a refusal had nowhere to land. Same for the property editor, where an edit that did not apply looked exactly like one that did - the field showed the new value and the class still said the old one.
-
Two lists that explain themselves. An empty tree with no explanation is indistinguishable from a broken one. The app list says what it collects and offers the two commands that fill it; the findings tree points at the workspace check. The app list also carries the system search in its title bar — the rest of the app landscape lives there, and looking for it started in the palette — and a tree item can hand you its class name to paste.
-
Other 24
Notes
-
"New Project from Template" now hands out the real template. The command wrote its own hand-typed copy of abap2UI5/app-template's files, and that copy had drifted: it installed
@abap2ui5/linter0.1.1 while the ecosystem was on 0.2.1, it pinned no framework release at all — so abaplint resolved abap2UI5's moving default branch and a rename over there could turn into a red build in your brand-new project — it left out thechain-house-layoutrule the guide tells you a gate applies, and it wrote noAGENTS.md, so an assistant opening the new project found nothing to read.A project created from the IDE now gets app-template's own
abaplint.jsonc,abap2ui5lint.jsonc,AGENTS.md(the complete app-building guide),.claude/settings.jsonpermission allowlist,.gitattributes,.gitignoreand Dependabot config, the pinned versions from itspackage.json, and a CI workflow running the abap2UI5-linter's own GitHub Action at the same pin the template uses. It is still generated rather than cloned — the command works in vscode.dev, where there is no git — but from a snapshot that is regenerated weekly and gated by the test suite, so it cannot quietly drift again. -
An opt-in rule your
abap2ui5lint.jsoncswitches on is now reported in the editor too.chain-house-layout— the builder-chain layout rule abap2UI5/app-template enables — was never produced by the in-editor gate, whatever your config said, so CI flagged a drifted chain and the editor stayed silent about it. That was the last piece of the editor/CI split. The first thing it turns up is in this extension's own gallery: the empty template's chain was laid out flat, and it is now indented per level like the rest. -
Fixes from a pass over the whole extension. Nothing here is new behaviour; all of it is behaviour that was supposed to work already.
The preview survives a system that drops a connection. A SAP system resetting the socket while an answer was still arriving took the whole extension host down with it. Loads the preview cancels (every reload cancels a few) now stop the request on the system too, instead of leaving it working on an answer nobody is waiting for. Session cookies from systems configured for cross-site use are no longer dropped, a logon page in a single-byte charset keeps its umlauts, and the proxy's own token stays out of the output channels — it used to ride along in the traffic log, where pasting the log into an issue handed over a working authorized url.
The render gate cannot pile up any more. It had no time limit and was never killed: on a slow line every save started another one and none of them ever went away. It now gives up after three minutes, or as soon as you have moved on from what it was rendering. On Windows it also works at all when your user name has a space in it — the scratch file arrived as two arguments and the gate answered "no JSON" for good — and
viewCheck.command/mcp.commandaccept a quoted program path.Completion, hover and the outline read
tag( `Button` ). The positional spelling is what "Convert XML View to Builder Chain" writes and what the samples use, and only half the extension understood it: converted code got no completions, and the outline showed every element as?.F2 rename finds both ends of a wire again. One apostrophe in one comment ("don't") used to hide every control id after it, so the rename went through on one end and left the other pointing at the old name — silently, which is the whole defect the rename exists to prevent. The formatter is no longer fooled by a commented-out
)->end( )either, and F2 on a type name no longer offers to rewrite everyTYPE stringin the class.Checks stop showing what is no longer true. Disabling a rule in
abap2ui5lint.jsoncused to leave the quick fix offering findings whose squiggles had already gone. "Check All Views in the Workspace" clears files that were fixed or deleted since the last run, checks each file once instead of twice, and says it was cancelled instead of reporting a whole-workspace result for part of one.The app keeps running when you collapse the panel. Ctrl+J used to reload it from scratch, losing the server session and any page it had navigated to. Changing the theme or language no longer leaves the toolbar and the app disagreeing about which url is loaded, and hiding the preview view no longer breaks the next save.
On vscode.dev, only what actually works is offered. The App Preview, the Apps tree and the Findings tree appeared there with nothing behind them and loaded forever; the web check now also honours
viewCheck.liveandviewCheck.onSaveand notices a baseline file that changed.Typing is cheaper. Inline annotations are debounced and no longer repaint when an unrelated extension touches its own diagnostics, completion reads the document once instead of twice, and the member and config lookups behind it are cached — the work per keystroke was several times what it needed to be on a long view.
Also: the last attribute of a chain can be removed in the property editor, a namespaced class shows up on the navigation map, two systems with the same name can both be selected, resetting credentials reaches systems removed from the settings, and re-running "Install Render Gate" without a network no longer deletes the gate you already had.
-
Three things the editor now says about a line unasked. One decoration pass, three ideas borrowed from the extensions that made them popular, all three fed by data this extension already ships.
The finding, at the end of its line — the Error Lens idea. A builder chain is forty lines long and the Problems panel is somewhere else; the squiggle is where you are looking and the message was not.
abap2ui5.inlineFindingstakesproblems(the default),alloroff; hints are out by default because "worth knowing, never wrong by itself" is a lot of grey text in a long chain.The UI5 version something arrived in — the Version Lens idea — warned when it is above your floor. Does my system have this yet? is the permanent abap2UI5 question and the answer has been sitting in the bundled metadata all along, arriving only as a finding once the floor was already crossed.
What a PUBLIC attribute costs per roundtrip — the Import Cost idea. abap2UI5 serializes every public attribute into the model on every single roundtrip; the size is measured from the class's own seeds or from the
<class>.mock.json, and an attribute nothing is known about still says that it is sent. A line that already carries a finding gets neither, because there the defect is what matters. -
Emmet for builder chains.
Page>content>Button*3expands to the chain that builds it —>child,+sibling,*nrepeat,#id,[a=b],{text}. Inside an existing chain the expansion continues it ()->ele( … ), no factory, noview_display( )); outside one it scaffolds the whole statement.>binds tighter than+exactly as in Emmet, soPage>content>Button+Inputputs both controls in the content aggregation — the obvious implementation, splitting on+first, makes the Input a sibling of the Page, which reads the same and builds a different view. The chain itself is written by the same emitter Convert XML View uses, so there is one opinion about the house layout rather than two. -
The apps of this workspace, as a list. Every toolchain extension has one — npm scripts, Docker, Maven, Jest all put the things of the project in a tree with a play button — and abap2UI5 had none: F9 works on the class you happen to have open, and Run a Recently Launched App only knows what this window already ran. Opening a repository with thirty apps in it, nothing said which thirty. The abap2UI5 Apps view lists every class implementing
z2ui5_if_appwith run, preview and check on the item. -
F2 shows the refactor preview. The rename crosses from ABAP into view literals on the strength of where a string stands, which is the one refactoring here worth looking at before it happens: every occurrence is listed with its line, and nothing changes until you say so.
abap2ui5.renamePreviewswitches it off for anyone who would rather just rename. -
A findings view, grouped by rule. The Problems panel lists findings per file, among every other contributor's — which answers "what is wrong in this file". The new abap2UI5 Findings view in the Explorer answers the other question: twelve
unknown-binding-pathacross three classes are ONE decision (fix, waive, baseline), and per file they look like twelve unrelated problems. Rules come worst-severity-first and then by count, so the top of the list is where the next decision is; expanding one lists every occurrence, clicking it opens the line, and the title bar carries the workspace check and the workspace fix. It reads the published diagnostics rather than checking again, so it costs nothing and cannot disagree with the squiggles. -
Extract to View Method. A view built with the builder is one statement, and a real screen is a long one; abap2UI5's answer is the handle idiom, a helper method taking the builder and returning it, which the linter follows so an extracted section stays reconstructable and checked. Doing it by hand means splitting one statement without breaking its parenthesis balance, inventing the parameter and remembering the declaration in the right section. The command computes all three: the head is captured into a handle (keeping its own name if it has one), the tail becomes
result = box->…inside a new method, and the call takes its place. It extracts a tail — a middle section would have to hand the handle back for the rest of the chain to continue from, which is neither the corpus idiom nor readable — and it refuses rather than guesses: a cursor outside a chain, a name already taken and a chain's own first call each come back with a sentence saying why. -
The preview shows data, several devices, and what changed. Three things that turned the systemless preview from a nice picture into the panel you leave open.
Preview data. The model behind a picture is derived from what the class seeds literally, so a table filled by a
SELECTphotographed as No data — which is most real apps, and made the preview least useful exactly where layout matters most. Azcl_app.mock.jsonnext to the class now fills it, merged over the derived model so two lines are enough to fill one table. The caption says which of the two the picture used: an empty table is a correct rendering of a model with no rows, and that has to be distinguishable from a broken binding.A device matrix.
abap2ui5.viewPreview.viewporttakes a list —390x844,1280x900— and renders every viewport in ONE browser session, side by side in the panel. The launch and the UI5 boot cost more than the renders, so the second device is nearly free, and responsive layout is precisely what nobody has in their head.Before and after. "Preview View - Compare with HEAD" renders the committed version of the file next to the working tree, from the same data. It answers the question no linter can: did my change do what I meant to the view? A file git does not know says so rather than comparing against nothing.
-
The panel tab says what is in it. With the default
openModeoftabthe app preview goes to an editor tab, so the bottom panel only ever shows the Control Properties form — under a tab labelled justabap2UI5, which named the extension rather than the thing on screen. It is abap2UI5 Control View now. (VS Code capitalises the first letter of a panel title itself, so it renders as Abap2UI5 Control View whatever the manifest says.) -
Preview the view without a system. An abap2UI5 view exists at runtime and nowhere else, so looking at one has meant a system: activate the class, launch the app, wait for the roundtrip — a long way to go to find out that a column is in the wrong order. "abap2UI5: Preview View (No System)" renders what the class builds and puts the picture beside it. No system, no transport, no activation, no launch URL.
It is the render gate turned around. That gate has been loading exactly this view in a real browser all along to decide whether it survives creation; the linter can now keep it standing and photograph it instead of throwing it away, and this command is that, wired to a panel: the reconstruction the view check validates, seeded with the model derived from the class's own
TYPES/DATA, in the theme (abap2ui5.viewPreview.theme) and viewport (abap2ui5.viewPreview.viewport—390x844for a phone) you pick. It re-renders on save, shows every view a class builds, and keeps the last picture up while the next one is taken. Render errors appear above the picture rather than instead of it: a view with one broken binding still comes up, and the half that rendered is the part worth seeing.It needs the render gate installed — the same runtime, one click — and it is not the app: nothing round-trips and no event reaches ABAP. That is what F9 is for.
-
F2 renames the strings the app is wired together with. An abap2UI5 app joins its two halves with literals: a control is
a( n =idv =TABLE)in the view andTABLEagain in aCONTROL_BY_IDwire; an attribute ismv_titlein the class and{/MV_TITLE}in the view. Nothing in ABAP or in UI5 connects those ends — to the compiler a string is a string — so renaming one of them has been a grep, and missing one is silent: a wire that addresses nothing does nothing at runtime, not even a console line, and a binding path that resolves to nothing renders empty. F2 already did this for event names; it now does it for control ids (theidattribute plusCONTROL_BY_ID,SET_FOCUS,SCROLL_TO,SCROLL_INTO_VIEW,KEYBOARD_SET_MODEandpopover_display( by_id = … )) and for bound attributes (the ABAP name and every binding path that resolves to it, including the root segment of a path into a table row).What makes it safe is that position decides what a literal is, never its text: the
setBusysitting next to the id in the same wire is an argument and stays put, and a name the class does not declare offers no rename at all. -
The browser build reads the repository's
abap2ui5lint.jsonc. It could not before, for a reason that was true of the reading and not of the file: discovery goes throughfs, which a browser extension host does not have. So vscode.dev and github.dev checked against the VS Code settings alone and quietly disagreed with CI about the UI5 floor, the distribution, the allow list and every rule severity — the one divergence the web build knowingly kept. The configs are now read through the editor's own filesystem API, the nearest one above a file governs it (what the CLI's upward walk decides, answered against the flat list a workspace search returns), and abaselinethe config names is applied too. What a config MEANS is now one piece of code for both builds, because two builds disagreeing about precedence is the same defect in a different place. -
Examples for the control under the cursor. The bundled UI5 metadata says what
sap.m.Tablehas; it cannot say what a working one looks like in an abap2UI5 class. Three repositories can — samples, samples-controls (416 ports of the official demo kit) and samples-stack — and until now the only thing in this window that could read them was the MCP server, on behalf of an agent. "Show Examples for this Control" asks them the same question with the cursor as the query, and offers the hits richest first: the number of attributes each example configures decides the order, and no single file may fill the list, so one app that happens to useTextforty times is not the whole answer. It reads the catalogues fromabap2ui5.mcp.reposRoot, and when none is checked out it says exactly that instead of reporting "nothing found" for a corpus that was never there. -
The view check has a line in the status bar. Its findings go to the Problems panel, next to whatever the ABAP extension, the XML language server and every other contributor puts there — so "is the file I am looking at clean?" meant opening a panel and reading past other people's entries. The status bar now answers it in place: counts per severity for the active file, the wrench count of what an autofix would correct, the warning or error colour behind it, and a click that opens the list the numbers came from. A clean file says so rather than going blank, because silence is indistinguishable from a check that never ran. It reads the published diagnostics rather than running the gate again, so it changes exactly when the check has something new to say.
-
The fixes and the baseline reach the whole workspace. Two commands that only existed one file at a time, in the cases where a repository is what you are actually working on. "Fix All View Findings in the Workspace" sweeps every checkable file and applies every mechanical correction as one WorkspaceEdit — one undo step, the editor's own refactor preview, unsaved buffers respected instead of overwritten — which is what adopting the linter on an existing repository, or a rule whose autofix arrived after the code did, actually needs. And "Rebuild the View-Check Baseline" writes the baseline file from what the workspace reports right now: the editor's
--update-baseline, for the moment when the lightbulb's one-finding waiver is hopeless because there are four hundred of them. It replaces rather than merges (the CLI fails on a stale entry, so a baseline that only grows is one nobody can keep green), asks before it writes, and says so when the config names no baseline — the CLI only honours one the config points at. Both share the check's own sweep, so "what the workspace says" means the same thing in all three. -
An autofix lens next to the check that finds the work. The class definition already carried Run, Activate & reload and Check views; what it did not carry was the one action that needs no reading at all. Autofix n findings now appears beside them whenever the view check has corrections it can apply mechanically — the obsolete binder method, the missing
$in an event argument, the ABAP boolean written straight into the view — and runs exactly what the palette's Fix All View Findings in This File andsource.fixAll.abap2ui5run. It carries the count and disappears at zero, so it never offers a press that would answer "nothing here can be corrected mechanically", and it follows the findings rather than the keystrokes: a changed setting, an adopted baseline or a finished check updates the number. Findings whose correction would have to guess are not counted — those stay a lightbulb decision on the line itself. Switch the lens row off withabap2ui5.codeLens, as before. -
The linter pin moves to 0.2.1, and the MCP server gets the two catalogues it grew. The pinned linter stops reading the
ELSEof aCOND #( ... )as the end of a lifecycle branch, which is the false positive that kept the sample corpora and the documentation on the longerIF check_on_init( ) OR check_on_navigated( )dispatcher. The render-gate bundle for that commit is published, so the pinned-bundle path stays exact.ai-mcp'sexamplestool searches three sample repositories now (samples,samples-controls,samples-stack) rather than one, soSAMPLES_HOMEandSAMPLES_STACK_HOMEare passed from the repos root alongside the two that were already there. A catalogue this extension does not point at is not an error over there — the tool answers from the ones it can read — so a missing variable would have cost a third of the answer silently. -
The scaffolded configs are now checked by the tools that read them. Every template's SOURCE was already linted, but the files written AROUND it were not: a rule id the linter later renames, or a stray comma in the JSONC, would have turned New Project from Template into a project that fails on its first
npm run check— with nothing in this repository noticing. The linter's ownloadConfignow parses the generatedabap2ui5lint.jsonc,abaplint.jsonchas to parse, and everynpm run <x>a generated script chains has to name a script that exists. -
New Project from Template. New App from Template hands you a class, which is the right thing when you already have a repository — and leaves you with the one artefact the extension cannot check properly when you do not. Without an
abap2ui5lint.jsoncthe view check falls back to VS Code settings, so the first CI run in that repository disagrees with what the editor had been telling you. The new command scaffolds the whole thing into an empty folder: the class and its abapGit sidecars,abap2ui5lint.jsoncandabaplint.jsonc, apackage.jsonwhosenpm run checkis exactly what the workflow it also writes runs, and a README. Same three templates, same linter, and the config the editor reads is now the config CI reads. It refuses a folder that already looks like a project rather than writing over it. abap2UI5/app-template stays the fuller starting point — the notification links there. -
The auth proxy only answers its own preview. While a system is connected the extension runs a small proxy on 127.0.0.1 that adds your credentials to every request it forwards, so the app in the preview never sees a logon prompt. It used to add them for any caller: the port is random, but a random port is a scannable port, and a web page you happen to have open can reach loopback too — so anything on your machine could have driven an authenticated session against your SAP system through it, ADT included. Now every url the proxy hands out carries a per-session token, requests without it are answered 404, and a request that arrives under a hostname other than loopback — what a DNS rebinding attempt looks like from this side — is refused outright. Nothing to configure: the preview, Open in Browser and the MCP tools all get the token as part of the address.
-
A hung system no longer hangs the preview. A forwarded request that gets no answer within two minutes is dropped with the proxy's own 502 instead of being held open for as long as the socket lives. Credentials are also cleared when the proxy stops, rather than lingering until the next connect.
-
The abap2UI5 logo, everywhere the extension shows an icon. The generic placeholder is gone: the red abap2UI5 disc is now the Marketplace tile, the icon of the abap2UI5 tab in the bottom panel, and the tab icon of the app preview and the navigation map. The Marketplace tile carries the full logo; the three small icons are 16 and 24 pixels wide, where two lines of wordmark are a smudge, so they carry a short mark — the logo's own a and 2, in the logo's own type, side by side on the disc. The panel tab has them punched out of the disc rather than painted on it, because VS Code tints that icon with the theme colour and keeps only its silhouette.
-
Other 1
Notes
- TLS verification is configurable. The auth proxy used to accept any
certificate from the system unconditionally; the new setting
abap2ui5.allowUnauthorizedCertsmakes that a choice. It stays on by default — SAP development systems typically serve self-signed certificates — but if your system presents a properly trusted certificate, turn it off and the proxy verifies the chain on every request, closing the machine-in-the-middle window the default leaves open. The setting description spells out the tradeoff.
- TLS verification is configurable. The auth proxy used to accept any
certificate from the system unconditionally; the new setting
Other 10
Notes
- Traffic log with roundtrip timings. The auth proxy sees every request the embedded app makes; the new abap2UI5 Traffic output channel logs each one with method, status, size and the full roundtrip duration, and the preview toolbar shows the last backend POST's timing as a badge (warning-coloured from 1 s). "Is the backend slow or the UI?" stops being a devtools trip.
- Take App Screenshot. The running app as a PNG, opened beside the code with Save As… one click away — rendered by the render gate's headless Chromium through the same auth proxy, so no login page and no browser. Command, plus the 📷 button in the preview toolbar.
- Stateful reload. The 📌 toolbar toggle captures the app's JSON model right before a reload and restores it into the fresh page — only the paths the class itself declares (the same derived model completion already uses), so framework state stays with the fresh load. A popup-deep test state survives the activate-reload loop.
- Control Properties panel. The builder control under the cursor as an
editable form, next to the App Preview: written attributes with enum
dropdowns from the UI5 metadata, expression values read-only, an add-row
for every member the control accepts but does not set. Every change is an
ordinary text edit of the
a( )calls — undo works, the linter re-checks. With Inspect: click the control in the app, adjust it in the form. - Colour swatches. A colour written into a colour-typed property
(
sap.ui.core.CSSColorand friends) gets VS Code's inline swatch and colour picker — in builder chains and raw view XML, on the web too. - Convert XML View to Builder Chain. The reverse of the reconstructed
XML view: paste a UI5 demo kit sample (selection, document or clipboard)
and get the
z2ui5_cl_ai_xmlchain in the corpus style — Format Document is a no-op on the result, and whatever the builder cannot express becomes a TODO comment instead of a silent drop. Porting starts with paste. - New App from Template. The single skeleton grew into a gallery: empty view, list, form, master & detail, popup — pick one, name the class, and it lands at the cursor or as a new document. Every template passes the bundled linter in the test suite, so the gallery cannot teach what the linter reports.
- App navigation map. "Show App Navigation Map" draws every
z2ui5_if_appclass in the workspace and eachnav_app_call( )between them as a clickable graph — apps nothing navigates to on the left, unresolved targets dashed. Click a node, land in the class. - Fixed: the
z2ui5tableandz2ui5popupsnippets' chains were unbalanced. Their mid-chain)->shut( )segments closed one parenthesis too many - the linter's reconstruction scan tolerated it, a real system would not have. A mid-chain shut now stays open for the next line's)(the corpus style), and a new test pins the paren balance of every shipped snippet and template so it cannot regress. - The abap2UI5 System MCP server. ai-mcp stays the system-less
sandbox; the extension now also offers real-system tools it alone can
provide (configured systems, stored credentials, auth proxy):
list_systems,search_apps(ADT quick search) andrun_app— the app rendered on the system, returned as a screenshot. Hosted in-extension over HTTP on 127.0.0.1, disabled withabap2ui5.mcp.system: false.
Other 12
Notes
- The client API is known before the fact.
client->completion offers everyz2ui5_if_clientmethod, and hovering a call shows its full ABAP signature and documentation - parsed from the interface source and bundled, sopopover_display( xml = … by_id = … )is offered correctly instead of corrected afterwards. Triggered by the>of the arrow. - Format Document repairs a builder chain. The chain IS the view
hierarchy, so its indentation is structure, not taste: a child one step
under its parent, an attribute one step under its element, a
shut( )on the level of theopen( )it closes. Only lines beginning with a builder verb inside a chain are touched - comments, multi-line values and everything else keep their bytes. Verified as a no-op against the canonical corpus style. - Baseline support - adopt the linter without fixing everything first.
When
abap2ui5lint.jsoncnames abaselinefile, the editor drops exactly the findings CI drops and logs how many. A new quick fix "add to baseline" appends the finding under the cursor to that file (same line-free keys as the CLI's--update-baseline), and the squiggle disappears immediately. - Render errors land on their line. The render gate reports message strings, which used to squiggle line 0 of every file. The token the message quotes (a value, a control name) is located in the source and the error is placed there; only a message quoting nothing keeps line 0.
- More rules run in the editor. The in-process gate now hands the class's
own control ids and root fields to the linter, enabling the
CONTROL_BY_IDwire checks andrelative-binding-without-context- and attaches theundeclared-namespacequick fix for conventional prefixes (xmlns:l,xmlns:core, …), exactly like the CLI. - Method navigation for the ABAP file. F12 on a method call jumps to its
METHODimplementation in the class, and the workspace symbol search (Ctrl+T) finds method implementations across every*.abapfile. abap2ui5lint.jsoncvalidates while you edit it. The linter's own config schema ships with the extension (contributes.jsonValidation), so an unknown key or a misspelled rule id squiggles in the config file, offline.- New snippets.
z2ui5main(lifecycle dispatch),z2ui5popup,z2ui5popover,z2ui5toast,z2ui5msgbox,z2ui5navto,z2ui5navback,z2ui5modelupdate,z2ui5eventarg- and every shipped snippet (plus the app template) now has to pass the bundled linter in the test suite, so a snippet can no longer teach what the linter reports. - Obsolete client methods are marked. The
client->completion strikes through what the interface's own abapdoc declares obsolete (_bind_edit,nest_view_model_update,nest2_view_model_update), sorts it last, and the hover leads with the warning - the deprecation reaches you while typing, not from the linter afterwards. - Warned when the system outruns the metadata. When the detected system UI5 is newer than the bundled snapshot, the output channel says so - a genuinely new control would be reported as unknown, and now the caveat is on record instead of a mystery.
- Activation covers view files. The extension also activates for XML
files and workspaces containing
*.view.xml/*.fragment.xml, so raw view checking works without opening an ABAP file first. - Fix-on-save, documented. Opt in with
"editor.codeActionsOnSave": { "source.fixAll.abap2ui5": "explicit" }- every mechanical fix is applied on save, same as the CLI's--fix.
- The client API is known before the fact.
Other 8
Notes
- Inspect: click the app, land in the code. The 🎯 button in the preview
toolbar starts a one-shot inspect mode: the hovered control is outlined,
a click jumps to the
open( )/leaf( )call that wrote it (Esc cancels). The clicked control's type and parent chain are matched against the reconstructed view, so a row inside a bound list lands on its template - and anidwritten in the class settles the match outright. - The app's model, one click away. The
{ }button asks the running app for its JSON model and shows it as a document beside the code - the live values next to the statically derived shape completion and hover already know. Every click refreshes the same document. "Why is that field empty?" is now: look. - The XML preview follows the editor. One preview, like the Markdown preview: switch to another view-building class and the XML swaps to that class, edit and it re-renders after each pause. A class that builds no views leaves the last reconstruction standing instead of blanking the tab.
- A binding path jumps to its declaration. Go to Definition on
{/MT_TRAVELS/STATUS}lands on thestatusline of theTYPES BEGIN OFblock (or theDATAline for a root path) - the fourth corner of the square: completion offers it, hover judges it, the gate reports it, and now F12 goes there. WHENbranches count their raises. A CodeLens over everyWHEN '…'the view actually raises says "raised n× in the view" and peeks the_event( )calls. AWHENnothing raises stays unannotated - the CASE may switch over something else entirely.- Rename an event everywhere at once. F2 on an event name - in
_event( 'GO' )or on theWHEN 'GO'- renames every raise and the handler together, so the view and the dispatch cannot drift apart. - The detected UI5 version stays visible. After the first launch the
status bar shows
UI5 1.xxxfor the active system; clicking it opens the view-check settings. - CI proves the web build.
npm run test:webactivates the web bundle in a real headless browser extension host (@vscode/test-web) and fails the build when a module drags a node builtin into the web graph or the activation throws - the gap the node-based suite could not cover.
- Inspect: click the app, land in the code. The 🎯 button in the preview
toolbar starts a one-shot inspect mode: the hovered control is outlined,
a click jumps to the
Other 10
Notes
- The reconstructed XML is navigable and honest about problems. The
reconstruction records which builder call wrote every node and attribute,
so the XML preview now uses it: Go to Definition on any line jumps to
its
open( )/leaf( )/a( )in the class, and the view check's findings are mirrored onto the XML lines they concern - the structure and what is wrong with it, in one place. - Event navigation. Go to Definition on the event name in
client->_event( 'GO' )jumps to theWHEN 'GO'that handles it - and on theWHENliteral it jumps back to every place the view raises the event. Until now that round trip was a text search by hand. - The view hierarchy in the Outline. A class building views gets its
open( )/leaf( )nesting as a labelled tree in the Outline pane (abap2UI5 view), with theida chain sets shown alongside - a long view method reads as a tree again, and the Outline clicks straight to the builder call. - Hover on binding paths. Hovering a
{…}path says what the derived model resolves it to - a field, a structure, a table (and that an aggregation binds it), a path under an undeclared DDIC type (accepted unchecked), or missing - the same verdict the gate'sunknown-binding-pathrule reaches, before it has to. - Runtime errors point back at the source. When a forwarded runtime
error names a binding path or a quoted identifier that appears in the
running class, the output channel adds the file and line right under the
error - for local files as a clickable
path:line. - The system says which UI5 it runs. After the first launch against a
system the extension reads its
sap-ui-version.json(with the credentials the proxy already holds) and, when version or distribution disagree with the view-check settings, offers once per system to adopt the answer. No more guessingminUi5. - "Run an App from the System." A picker backed by the system's ADT quick search: type part of a class name, pick, launch - no need to have the class open or to have run it before. (A name search - whether the class is an app, the launch shows.)
- Runs in the browser. The extension now ships a web bundle for
vscode.dev, github.dev and the browser-based SAP Business Application
Studio: completion, hover, binding paths, the in-process property gate,
quick-fix-free diagnostics, the reconstructed XML view, outline and event
navigation all work there - everything that needs no process and no
socket. The embedded preview, ADT integration, render gate and MCP server
stay desktop-only, and their commands hide from the palette on the web.
One knowing gap: the web check reads the VS Code settings only, not a
repository's
abap2ui5lint.jsonc. - Getting-started walkthrough. VS Code's Welcome page now carries the whole loop - launch URL, F9, Ctrl+F3, view check, reconstructed XML, MCP - as a six-step walkthrough. The Marketplace listing gains the categories Programming Languages and Linters.
- The linter pin moves by itself. A weekly workflow re-resolves the
pinned
@abap2ui5/lintercommit, runs the full gate over the result and opens a PR - new rules and metadata reach the editor without anyone remembering the bump (AGENTS.md calls it the release lever; now it pulls itself).
- The reconstructed XML is navigable and honest about problems. The
reconstruction records which builder call wrote every node and attribute,
so the XML preview now uses it: Go to Definition on any line jumps to
its
Other 3
Notes
- Runtime errors of the running app reach VS Code. The embedded preview
used to swallow them: a thrown error, a failed UI5 assertion, a rejected
promise were visible only in the browser devtools — exactly the context
switch the preview exists to avoid. The auth proxy now plants a small hook
into the app's HTML that forwards
window.onerror, unhandled rejections andconsole.errorto the extension: the full text lands in the abap2UI5 output channel, and the preview toolbar counts them in a red badge that clicking opens the log. The count resets with every (re)load. Applies totabandpanelmode —externalopens a real browser, which has its own devtools. - "Show Reconstructed XML View." abap2UI5 views are strings assembled by
builder calls, so what actually reaches
XMLView.createwas never visible. The linter reconstructs exactly that for its checks; the new command "abap2UI5: Show Reconstructed XML View" opens the same reconstruction as a read-only, syntax-highlighted XML document next to the class — and keeps it following the edits, refreshing shortly after each pause. Debugging a nesting or binding problem stops being "stare at the builder chain". - Completion for binding paths. The gate has always reported a path the
derived model does not have (
unknown-binding-path); now the paths it does have are offered while the binding is written. Typing{in ana( v = … )literal lists the model's paths — the fields of the enclosing aggregation's row first ({STATUS}inside a list bound to{/TRAVELS}, through nested aggregations too), absolute paths after (/TRAVELS/STATUSwalks through the table's row, the way the gate resolves it). Structures the class does not declare (DDIC types) are offered as themselves and never guessed into, named models and expression bindings are left alone — the same lines the gate draws. What is offered is exactly what will not squiggle afterwards.
- Runtime errors of the running app reach VS Code. The embedded preview
used to swallow them: a thrown error, a failed UI5 assertion, a rejected
promise were visible only in the browser devtools — exactly the context
switch the preview exists to avoid. The auth proxy now plants a small hook
into the app's HTML that forwards
Other 3
Notes
- The abap2UI5 panel is no longer a dead end. With the default open mode
(
tab) F9 opens the app in an editor tab, while the abap2UI5 view in the bottom panel kept showing "press F9, the app opens here" — an instruction that was never going to come true there. The empty state now says where the app actually opens, and names the app once one is running: ZCL_MY_APP is running in an editor tab, with Show it here and Go to the tab. - Move the preview without going through the settings. "abap2UI5: Show
the Preview in the Panel" and "abap2UI5: Show the Preview in an Editor
Tab" switch
abap2ui5.openModeand take the running app along, so it changes place instead of having to be launched again. The panel's title bar carries the way back, next to the reload button. - "abap2UI5: Go to the Running App" focuses the preview wherever it is.
- The abap2UI5 panel is no longer a dead end. With the default open mode
(
Other 2
Notes
- On the VS Code Marketplace. The extension is published as
abap2ui5.abap2ui5— install and update it from the Extensions panel like any other extension. The.vsixattached to every GitHub release stays available for offline installs. - The extension ID changed from
abap2ui5-local.abap2ui5toabap2ui5.abap2ui5: the placeholder publisher gave way to the real Marketplace publisher. A previous.vsixinstall keeps working but is a different extension to VS Code and will not update — uninstall it once (code --uninstall-extension abap2ui5-local.abap2ui5) and install from the Marketplace. Settings are kept (they live insettings.json); the stored SAP credentials are scoped to the extension ID, so they are asked for once again.
- On the VS Code Marketplace. The extension is published as
Other 20
Notes
- Completion and hover for every UI5 control and property. The extension
already ships the UI5 metadata its view check validates against - a complete
API reference sitting next to the bundle. It is now offered while the view
is written: control names inside
open( )/leaf( ), the properties, aggregations, associations and events of exactly that control inside thea( )chained to it, and the accepted values of an enum or boolean property. Hover shows the type, the UI5 version a member appeared in, its deprecation and a link to the UI5 API reference. Raw*.view.xmland*.fragment.xmlfiles get the same. No system, no network, no setup - the knowledge that reported the typo afterwards now prevents it. - Quick fixes. The findings whose correction is mechanical carry the
correction with them, and the lightbulb now offers it: the obsolete
client->_bind_edit( ), a missing$in an event argument, an ABAP boolean written straight into the view. "Fix all in this file" comes with it, also assource.fixAll.abap2ui5foreditor.codeActionsOnSave. - Waive one line, the way CI understands it. Every finding now offers
"suppress on this line", which writes the linter's own
" abap2ui5lint-disable-next-line <rule>directive above it. The other half of that was a real defect: the editor ignored those directives, so a line deliberately waived for the CLI and the GitHub Action kept squiggling here. It no longer does. - The editor reads
abap2ui5lint.jsonc. A repository that pins its UI5 floor, its distribution, itsallowlist or its per-rule severities was checked against those in CI and against the VS Code settings here - the same file could be clean in the editor and red in CI. The repo config now wins wherever it says something, the settings fill in the rest, and theallowlists merge. The output channel names the file the values came from. - Checking while you type. The property gate runs in-process, so it no
longer waits for a save: findings appear shortly after each pause. The
render gate stays on save and on demand. Switch it off with
abap2ui5.viewCheck.live. - "Check All Views in the Workspace" runs the same gate over every ABAP class and view file in the workspace and fills the Problems panel - the answer to "will the linter gate pass before I push?", which until now only covered whatever happened to be open.
- Every finding links to its rule. The diagnostic's code is the rule id and points at the published rule reference, so a Ctrl+click explains what the rule means and what the fix looks like. Deprecations are marked as such, so VS Code strikes the member through.
- More than one system.
abap2ui5.systemsholds named launch profiles; "abap2UI5: Select System" switches between them and remembers the choice per window, so two windows can work against two systems at once. Credentials are stored per host, so switching does not ask again - and an existing single-system install keeps working untouched. - Theme and language in the preview toolbar. Both are ordinary URL parameters of the app, so checking an app in Horizon Dark or in a second logon language is now two clicks instead of a hand-edited URL in the browser.
- A rejected logon says so. Inside the iframe a 401 was an unhelpful page, and the only cure was finding "Clear Stored SAP Credentials" in the palette. The proxy sees the answer, so the extension now offers to retype the credentials and reloads with them.
- Run an app without its class open. "abap2UI5: Run a Recently Launched App" lists what this window has launched.
- Run, Activate & reload and Check views above the class definition. The
dev loop was discoverable only through the palette; a CodeLens says it out
loud. Switch it off with
abap2ui5.codeLens. - Fixed: the view check did nothing in a fresh window. The extension declared no activation event, so it only woke up once F9 was pressed or the preview panel was opened - until then, saving an ABAP class produced no diagnostics at all. It now activates for ABAP files.
- Fixed: F9 did nothing for
INTERFACES: z2ui5_if_app.The chained form is just as common as the plain one and was not recognised, so F9 silently toggled a breakpoint instead of launching the app. - Fixed: two quick saves lost one check. The checker allowed one run at a time globally and dropped anything arriving while it was busy - with the render gate on, which takes seconds, that was easy to hit. Runs are now tracked per file, and only a newer run for the same file supersedes an older one.
- The app template and the snippets use
z2ui5_cl_ai_xml. They still taughtz2ui5_cl_xml_view, which is on its way out of abap2UI5 and which the view check deliberately does not reconstruct - so "Insert App Template" handed out a class the extension's own checker then ignored. Five more snippets come with the change (container, input, table, event dispatch, lint waiver). - Internal: the settings that name a program to start (
viewCheck.command,mcp.command,mcp.reposRoot) are machine-scoped, so a cloned repository cannot point them somewhere else; the CSP nonce comes from the crypto RNG; the pure helpers moved intovscode-free modules with anode --testsuite behind them, which CI now runs; CI and the release build on Node 22, the version the bundled linter asks for. - The repository moved from
abap2UI5-addons/vscode-extensiontoabap2UI5/vscode-extension, and the linter's fromabap2UI5/abap2UI5-lintertoabap2UI5/linter. GitHub redirects the old addresses, so nothing breaks - but the links, thenpxfallback and the render-gate download now name the repositories directly. - A linter checkout named
linteris found again. Both the MCP registration and the view checker probe#abap2ui5.mcp.reposRoot#for a checkout by directory name, and the list only held the two pre-rename names. Cloning the linter under its current name therefore produced a checkout the extension ignored: the render gate silently fell back tonpxand the MCP server started withoutAI_VIEW_CHECK_HOME.linteris now probed first, the old names still work. - Internal: the bundled
@abap2ui5/lintermoved up 19 commits, from the extraction commit to the current linter release. Six new rules come with it (CSS brace escaping in both its forms, frontend-action wire tokens, unresolved and out-of-range event args, dead PUBLIC attributes).
- Completion and hover for every UI5 control and property. The extension
already ships the UI5 metadata its view check validates against - a complete
API reference sitting next to the bundle. It is now offered while the view
is written: control names inside
Other 4
Notes
- Findings land on the right line. Diagnostics used to be placed by
searching the file for the first occurrence of a name - in a class with
ten buttons, the squiggle sat under the first one no matter which button
was broken. The linter now records where every finding came from, so the
diagnostic goes exactly there: the second duplicate
id, thea( )call that sets a property twice, the attribute that carries the typo'd value. - Three severities instead of two. Findings are now classified by the
linter itself:
error(the app breaks),warning(it works here, but not necessarily on the UI5 version your system runs) and - new - informational hints for things that are worth knowing but never wrong by themselves, such as an event nothing handles or an icon-only button without a tooltip. They no longer look like defects in the Problems panel. - The binding-path checks now run in the editor at all. The property
gate was called without the model derived from the class, and the rules
that need it stayed silent: a
{/TYPO}the model has no path for, and a table or structure bound to a scalar property. Both now show up on save, and inside a bound aggregation a relative{TYPO}is resolved against the row - so a misspelled field in a column template, which otherwise just leaves that column empty forever, is caught while you type it. - Two new checks, both of which dump before the app reaches the browser:
the same attribute written twice on one control, and
a( )on the barez2ui5_cl_ai_xml=>factory( )root with no element to attach it to.z2ui5_cl_ai_xmlasserts on both.
- Findings land on the right line. Diagnostics used to be placed by
searching the file for the first occurrence of a name - in a class with
ten buttons, the squiggle sat under the first one no matter which button
was broken. The linter now records where every finding came from, so the
diagnostic goes exactly there: the second duplicate
Other 8
Notes
- abap2UI5-specific checks - the defects that stay silent at runtime.
A hand-written binding path the model does not have (the field just
stays empty),
_bind( )on an event or_event( )on a property, a value bound to a local variable (lost after the roundtrip, because the instance is serialized and the method stack is not), an event nothing handles, and the obsoleteclient->_bind_edit( ). No UI5 tooling can see these - they live in the relationship between the ABAP class and the view it builds. Also caught: an ABAP boolean written straight into the view - it arrives as'X'/' ', and since UI5 reads any non-empty string as true,visible = abap_falsemakes the control visible. Wrap it inz2ui5_cl_ai_xml=>as_bool( ). - Deprecated properties and duplicate aggregations. Deprecation was only checked on control level; it now applies per property too, with the same target-version rule. And opening the same aggregation twice under one control - where the second tag silently replaces the first - is reported as an error.
- Three more silent failures caught: a view built but never
displayed (an empty page, no error), a
Tablebound to rows but given nocolumns, and a table or structure bound to a scalar property. - More view checks: a duplicate
id(a runtime error), a namespace prefix used but never declared, unbalanced braces in{= … }expression bindings, and two unambiguous accessibility defects (icon-only button without a tooltip, image withoutalt). - SAPUI5 or OpenUI5 (
abap2ui5.viewCheck.distribution). SAPUI5 ships libraries OpenUI5 does not -sap.ui.comp(Smart controls),sap.suite.*,sap.ushell,sap.fe,sap.viz- so a SmartTable is perfectly fine on SAPUI5 and a guaranteed runtime error on OpenUI5. Set it to what your system serves; withopenui5those controls are reported as errors instead of being skipped silently. - The target UI5 version now governs deprecations too
(
abap2ui5.viewCheck.minUi5). A control deprecated as of 1.149 is no longer flagged for a 1.71 target - only from the version its deprecation takes effect. The output channel logs the target version and the version the bundled metadata came from. - Self-installing render gate. "abap2UI5: Install Render Gate"
downloads the self-contained checker bundle (published by
abap2UI5-linter's CI) and Chromium into the extension's storage and runs
both with VS Code's own runtime - the render gate no longer needs
node, npm or any PATH setup on the machine. The command is also
offered directly from the warning when the gate is enabled but
missing, and installing enables
abap2ui5.viewCheck.render. - Fix: no more "view check passed" on files that only quote builder
code (e.g. a log file embedding class source). Checkability now
requires an ABAP source actually calling
z2ui5_cl_ai_xml=>factory(or a*.view.xml), and when nothing can be reconstructed the check says so instead of claiming a pass.
- abap2UI5-specific checks - the defects that stay silent at runtime.
A hand-written binding path the model does not have (the field just
stays empty),
Other 3
Notes
- Static view checks in the editor. Saving an ABAP class that builds
views with
z2ui5_cl_ai_xml(or a raw*.view.xml/*.fragment.xml) now runs the abap2UI5-linter gates and shows the findings in the Problems panel: controls that do not exist in UI5 at all (sap.m.Shell2- a typo, shown as an error), controls or properties newer than your UI5 floor (default 1.71), and deprecated controls. The property gate and its UI5 metadata snapshot are bundled with the extension - zero setup, instant, works offline, on documents from the ABAP remote filesystem (adtscheme) and on unsaved buffers. Optionally (abap2ui5.viewCheck.render) the external abap2UI5-linter (formerly ai-view-check) CLI adds real render errors from a headlessXMLView.create. On demand: "abap2UI5: Check Views (Static)". Configure the floor, accepted deviations and the render-gate command underabap2ui5.viewCheck.*. - The abap2UI5 MCP server, offered to every MCP client in the window.
The extension registers the
ai-mcp server as an MCP server
definition provider, so Copilot agent mode (and any other MCP client in
VS Code) can use the abap2UI5 dev loop without an SAP system:
capability queries, static view validation, deploy into the sandbox,
transpiled build, headless run returning page errors and a screenshot.
Point
abap2ui5.mcp.reposRootat the folder holding theabap2UI5,ai-demokit(and optionallyabap2UI5-linter,ai-mcp) checkouts; disable withabap2ui5.mcp.enabled. - The minimum VS Code version moved from 1.85 to 1.101 (June 2025) - the first release with the stable MCP server definition API.
- Static view checks in the editor. Saving an ABAP class that builds
views with
Other 4
Notes
- Fix: every button press in the embedded preview could fail with
"CSRF validation failed - cross-origin POST rejected". The embedded app
talks to the local auth proxy on
127.0.0.1, so the browser sent that asOrigin/Refererwhile the forwarded request carried the SAP host — a mismatch that origin-validating CSRF checks reject on every POST. The proxy now rewrites both headers to the system's own origin, so the roundtrips look same-origin to the server again. - Fix: the activation watch only ever started for
adt:documents. Any other way of editing a server-backed source never got the automatic reload after activation. The watch now starts on every save of the shown class — the server knows whether an inactive version exists, whatever the file's URI scheme is. (Sources that never reach a server simply never show up as inactive, so nothing reloads there either.) - Fix: an activation right after the save was missed. The watch used to wait for the class to show up as inactive before an active answer would count as the activation. Activating directly — where the save is part of the activation — can be finished on a fast system before the watch ever looks, so there was nothing inactive to see and the watch waited for a flip it had already missed; only a slow save-then-activate cycle ever reloaded. The watch now also remembers the change timestamp of the class the preview shows: active with a newer change timestamp is a finished activation, no matter how fast it went. (Sources that never reach the server keep their timestamp, so purely local saves still reload nothing.) The first look at the server also moved from 2.5 seconds after the save to a quarter of a second, with checks every 1.5 seconds after that.
- New output channel
abap2UI5(View → Output): the activation watch says what it is doing there — started, class inactive, active again → reload, or the reason it stops (ADT unreachable, timeout). When an automatic reload does not happen, this is the place that says why.
- Fix: every button press in the embedded preview could fail with
"CSRF validation failed - cross-origin POST rejected". The embedded app
talks to the local auth proxy on
Other 4
Notes
Reloading after activation now actually happens — however you activate (#5).
- Activations are detected on the server. 0.9.0 only reloaded on its own
Ctrl+F3; activating with the ABAP remote filesystem's own button or shortcut
went unnoticed, because VS Code has no event for it. Now, while the preview
shows the not activated badge, the extension watches the class on the
server (its ADT metadata, fetched with the credentials it already holds for
the preview) and reloads as soon as the inactive version is gone — no matter
whether the activation came from Ctrl+F3, the ABAP extension's own UI or
even Eclipse. Needs the source to be opened from a system (scheme
adt) and the ADT services answering on the launch-URL host; where they do not, the watch stops silently and the badge stays until you reload. - Fix: Ctrl+F3 (and the ⚡ button) could stay dead for a whole session.
They were gated on a "an ABAP extension with an activate command is
installed" flag computed once at startup — when this extension happened to
activate before the ABAP extension had registered its commands, the flag
stayed false and the key silently did nothing. The gate is now simply "the
file was opened from an ABAP system" (scheme
adt), which implies working ABAP tooling and cannot go stale. - The not activated badge is now clickable — it reloads the preview right there (showing the still-active version, as the tooltip says).
- Ctrl+F3 now hands the exact document to
abapfs.activateinstead of relying on its active-editor fallback.
- Activations are detected on the server. 0.9.0 only reloaded on its own
Ctrl+F3; activating with the ABAP remote filesystem's own button or shortcut
went unnoticed, because VS Code has no event for it. Now, while the preview
shows the not activated badge, the extension watches the class on the
server (its ADT metadata, fetched with the credentials it already holds for
the preview) and reloads as soon as the inactive version is gone — no matter
whether the activation came from Ctrl+F3, the ABAP extension's own UI or
even Eclipse. Needs the source to be opened from a system (scheme
Other 3
Notes
The preview now reloads when you activate, not when you save (#5).
- No more pointless reload on save. Saving an ABAP class does not change what the server runs — the activation does. A save of the shown class no longer reloads the preview; it marks it with a small not activated badge in the toolbar instead, so it is clear why the app still shows the old version.
- New command
abap2UI5: Activate and Reload Previewon Ctrl+F3 (Cmd+F3on macOS, the activation key from SAP GUI) and as a ⚡ button in the editor toolbar: it saves the class, hands the activation to the ABAP extension you already use (ABAP remote filesystem) and reloads the preview afterwards. The key is only taken over for objects opened from a system while such an extension is installed — everywhere else Ctrl+F3 keeps its usual VS Code meaning. - New setting
abap2ui5.reloadOnwithactivation(default),saveandnever. It replacesabap2ui5.reloadOnSave, which is deprecated but still honoured while the new setting is unset —falsebehaves likenever,truelikesave.
Other 11
Notes
A visual pass over everything the extension shows.
- New preview toolbar with the class name, the system URL, a status dot (loading / ready) and buttons for reload and "open externally". It follows your colour theme instead of bringing its own colours.
- Device widths: switch the preview between desktop, tablet (834px) and phone (414px) to check a responsive app without leaving the editor. The choice is remembered per preview.
- Loading state: a spinner and "Starting ZCL_…" instead of a white area. If the app takes longer than 12 seconds, the preview offers Reload and Open externally right there.
- Welcome screen in the preview panel: the three steps to a running app, plus buttons for the launch URL, the app template and the project page.
- Status bar entry while an app is running — shows the class, click reloads.
- Run button in the editor toolbar for ABAP files, next to the usual actions.
- New commands:
abap2UI5: Reload Preview(also in the preview panel's title bar) andabap2UI5: Set Launch URL, which validates the URL and the{class}placeholder before saving it. - Save toast: reloading after a save says so in the preview, so a slow round trip is not mistaken for nothing happening.
- Extension icon, tab icon and panel icon reworked.
- The
.vsixis now a download. Every release attaches the packaged extension to its GitHub release, so installing no longer means cloning and building. Every push and pull request also builds one as a CI artifact. - Internal: the webview HTML moved to
src/webview.tsand the inline scripts now run under a CSP nonce instead ofunsafe-inline.
Other 2
Notes
- The project is now English-only. README, changelog, code comments and every user-facing string (command titles, settings descriptions, input prompts, error messages, the webview placeholder) were translated from German to English. The convention is written down in AGENTS.md so it stays that way.
- Command titles changed accordingly, e.g. "abap2UI5: App starten (F9)" →
abap2UI5: Run App (F9). Command IDs, setting keys and behaviour are unchanged, so yoursettings.jsonand any custom keybindings keep working.
Other 3
Notes
- Renamed: the extension is now simply abap2UI5 instead of "abap2UI5 Demokit Helper". It was never tied to the demokit — the name only came from the repository it originally lived in. It now has its own repository: https://github.com/abap2UI5-addons/vscode-extension.
- README reworked: describes the extension as a general tool for abap2UI5 development, with tables for settings and commands.
- Command IDs unified under the
abap2ui5.prefix:abap2ui5-demokit.newApp→abap2ui5.newApp,abap2ui5-demokit.openDemokit→abap2ui5.openHomepage. The latter has always opened the abap2UI5 repository, and its title now says so.
When updating: the rename changes the extension ID from
abap2ui5-local.abap2ui5-demokittoabap2ui5-local.abap2ui5. VS Code therefore treats the new.vsixas a separate extension — uninstall the old one once:code --uninstall-extension abap2ui5-local.abap2ui5-demokit. Your settings (abap2ui5.*) survive, they live insettings.jsonand not on the extension ID. The SAP credentials in the SecretStorage do hang off the ID, so they are asked for once more on the first F9 after the update.Other 1
Notes
- Auto-reload on activation: saving/activating the app class shown in the
tab reloads the embedded browser automatically — no F9 needed. Can be turned
off with
abap2ui5.reloadOnSave(default: on).
- Auto-reload on activation: saving/activating the app class shown in the
tab reloads the embedded browser automatically — no F9 needed. Can be turned
off with
Other 1
Notes
- Fix: after F9 the cursor really does stay in the source. The loading UI5 app
pulls focus asynchronously — the extension now catches that for a short time
window (
onDidChangeViewState) and hands focus back to the code.
- Fix: after F9 the cursor really does stay in the source. The loading UI5 app
pulls focus asynchronously — the extension now catches that for a short time
window (
Other 1
Notes
- After a launch or reload, F9 returns focus to the source — the cursor stays where it was and you can keep typing right away.
Other 1
Notes
- F9 now reliably refreshes the existing tab/panel (reloading the app) instead of opening a new one. For a different class the existing tab switches to the new app. The reload runs as a message to the iframe.
Other 1
Notes
- Fix: blank/white app in the tab — the proxy now strips
X-Frame-Optionsand the CSP directiveframe-ancestorsfrom the SAP responses, so the browser allows embedding in the iframe.
- Fix: blank/white app in the tab — the proxy now strips
Other 5
Notes
- Embedded app with login through a local auth proxy: F9 shows the app in an editor tab (or panel) and the proxy injects the SAP credentials — no more 401.
abap2ui5.openModeextended:tab(the new default),panel,external- Credentials are asked for once and kept in the SecretStorage
- New command: "abap2UI5: Clear Stored SAP Credentials"
- The proxy forwards UI5 resources, cookies, CSRF and redirects transparently; self-signed HTTPS certificates are accepted
Other 2
Notes
- New setting
abap2ui5.openMode(external|panel), defaultexternalexternal: F9 opens the app in the normal browser (uses the SAP session/SSO)panel: embedded in the panel (only without interactive login, otherwise 401)
- URL normalization: duplicate slashes in the path are removed
- New setting
Other 4
Notes
- F9 launches a
z2ui5_if_appclass in the embedded browser panel at the bottom - New setting
abap2ui5.launchUrlTemplate(placeholder{class}) - Panel view "abap2UI5 / App Preview" with an "Open externally" fallback
- F9 on non-app ABAP files keeps the normal breakpoint behaviour
- F9 launches a
Other 4
Notes
- First version
- Command: "abap2UI5: Insert New App Template"
- Command: "abap2UI5: Open Demokit in Browser" (renamed in 0.6.0)
- Snippets:
z2ui5app,z2ui5button
Others providing
- abap-adt-apiabap-adt-apiA JS/TS client for the ADT resources an ABAP system exposes, so a program outside the system can do against it what the maintainer calls a good share of what the Eclipse tools do.independent, abapJS/TS client for the ADT resources of an ABAP system
- abap2UI5 Playgroundabap2UI5 PlaygroundABAP in the editor on the left and the abap2UI5 app it builds on the right, both inside one browser tab: the framework is transpiled to JavaScript at build time and the class in the editor is compiled when Run is pressed.community org, abapIn-browser ABAP editor beside the abap2UI5 app it builds
- vscode-tabl-ddicvscode-tabl-ddicA VS Code extension that registers .tabl.ddic files, the DDIC DDL form of an ABAP table definition, as a language of their own with a TextMate grammar for syntax highlighting, and nothing more.community org, abapVS Code syntax highlighting for .tabl.ddic table definitions
- HammerHammerA native macOS code editor for ABAP and CDS, with an Object Navigator, Transport Organizer, Data Browser and Gateway Client in the same app as the editor.independent, abapNative macOS code editor for ABAP and CDS
- @arc-mcp/adt-ls@arc-mcp/adt-lsA TypeScript SDK over adt-ls, the headless language server SAP ships inside ABAP Development Tools for VS Code, so a program drives it without the discovery, JVM and handshake work.independent, abapTypeScript SDK over SAP's headless adt-ls language server
- abapthemesabapthemesThree colour themes for ABAP source in ABAP Development Tools for Eclipse, shipped as an Eclipse plugin and installed from the maintainer's own update site.independent, abapThree colour themes for ABAP source in ADT for Eclipse
- Cloud ALM Link for ADTCloud ALM Link for ADTAn Eclipse plugin that shows the transports and SAP Cloud ALM features behind an ABAP object, and turns Cloud ALM IDs in source comments into links.independent, abapEclipse plugin linking ABAP transports to SAP Cloud ALM features
- ABAP Call HierarchyABAP Call HierarchyAn Eclipse plugin that shows which members a class method, function module or subroutine calls, read from its own ADT backend in the system and published on a beta update site only.independent, abapEclipse plugin showing the call hierarchy of ABAP members
- abap-call-hierarchyabap-call-hierarchyThe ADT backend needed by the separate ABAP Call Hierarchy Eclipse plugin, installed in the ABAP system with abapGit and still in beta, which its maintainer calls not feature complete.independent, abapADT backend for the ABAP Call Hierarchy Eclipse plugin
- abap-tags-backendabap-tags-backendThe ADT backend needed by the separate ABAP Tags Eclipse plugin, installed in the ABAP system with abapGit; an upgrade to v2.0.0 or later runs a migration program for its new database tables.independent, abapADT backend for the ABAP Tags Eclipse plugin
- ABAP TagsABAP TagsAn Eclipse plugin for putting arbitrary tags on repository objects, with a Tag Manager view, tagged object search and tag import and export, served by its own ADT backend in the system.independent, abapEclipse plugin for tagging ABAP repository objects
- BellaBellaAn Eclipse plugin that puts Claude into ABAP Development Tools: explain, generate, rework and implement from the editor's context menu, completion as ghost text, and a chat whose tools reach the system through the developer's existing ADT logon.independent, abapEclipse plugin bringing Claude into ABAP Development Tools
- ABAP Syntax HighlightingABAP Syntax HighlightingA VS Code extension that registers .abap files as the language abap and colours them with a TextMate grammar.independent, abapVS Code syntax highlighting for .abap files
- abapfs_extensionsabapfs_extensionsABAP imported with abapGit that sets the stateful session flag on systems older than 7.51, so an HTTP client of the ADT resources can update objects there.independent, abapABAP objects enabling ADT updates on systems older than 7.51
- ABAP remote filesystemABAP remote filesystemA Visual Studio Code extension that opens an ABAP system over ADT as an editable filesystem and exposes system tools to GitHub Copilot and external MCP clients.independent, abapVS Code extension editing ABAP systems over ADT as a filesystem
- ABAP Development Tools for EclipseABAP Development Tools for EclipseSAP's Eclipse plugins for ABAP: source, dictionary and CDS editors, Project Explorer, Outline and ABAP Unit, calling ADT resources on the connected system.sap official, abapSAP's Eclipse plugins for ABAP development over ADT
- ABAP Development Tools for VS CodeABAP Development Tools for VS CodeSAP's ABAP tools in VS Code: editing, debugging, ABAP Unit, ATC and transports for classes, CDS and RAP objects, over RFC to an on-premise or Private Cloud system and HTTP to a cloud one, with Joule's ADT MCP server and code completion behind an AI-unit licence.sap official, abapSAP's ABAP tools in VS Code for editing, debugging, ATC and transports
- my-ide-actionsmy-ide-actionsA collection of eight ABAP Development Tools IDE Actions covering class creation, conversion of classic Open SQL to ABAP SQL, code snippets and RAP tooling.independent, abapCollection of ABAP Development Tools IDE Actions
- @abap2ui5/mcp-server@abap2ui5/mcp-serverAn MCP server that puts the abap2UI5 development loop on the machine an agent runs on: check the view the ABAP builds, photograph it, deploy the class into a sandbox, boot the app headless and read the errors.community org, abapMCP server for the abap2UI5 check, deploy and run loop
- abapsmithabapsmithAn MCP server that lets an LLM develop in ABAP on a live system over ADT, with writes off by default and every mutation recorded in a local journal that can undo it.independent, abapABAP MCP server over ADT with a local journal that can undo writes
- btp-sap-odata-to-mcp-serverbtp-sap-odata-to-mcp-serverExposes every OData service on an S/4HANA or ECC system as MCP tools, in three discovery levels so a large catalogue does not have to be loaded at once.independent, btpExposes S/4HANA or ECC OData services as MCP tools
- midasol/sap-mcp-servermidasol/sap-mcp-serverA Python MCP server for SAP Gateway with four read-only tools that authenticate, query, fetch one entity and list services, and one of the few here that publishes its test results.independent, btpPython MCP server for SAP Gateway OData operations
- odata_mcp_goodata_mcp_goA Go binary that bridges any OData v2 or v4 service to MCP — and can collapse the whole service into one tool when the generated tool list is too large to be usable.independent, btpGo binary bridging any OData v2 or v4 service to MCP
- odata_mcpodata_mcpThe Python original: reads an OData v2 service's metadata and generates MCP tools from it, with the SAP-specific conversions the raw protocol leaves to you.independent, btpPython bridge generating MCP tools from OData v2 metadata
- sap-mcp-serversap-mcp-serverReads SAP ECC and S/4HANA for an agent over Gateway OData and RFC, with a Node bridge that speaks the RFC protocol without the proprietary SAP NetWeaver RFC SDK.independent, abapReads SAP ECC and S/4HANA for an agent over Gateway OData and RFC, with a Node bridge that speaks the RFC protocol without the proprietary SAP NetWeaver RFC SDK.
- Skybuffer AI MCP serverSkybuffer AI MCP serverAn ABAP add-on that serves SAP business data, transactions and development context as MCP tools from an ICF node inside SAP ECC and S/4HANA, each call running as the signed-in user under PFCG authorization checks.independent, abapABAP add-on serving business data and transactions as MCP tools
- sap-security-mcpsap-security-mcpAn entirely read-only security analysis surface: nineteen tools, five resources and eight prompts for audits, SOX and segregation-of-duties review, behind OAuth 2.0 with PKCE. The vendor hosts an endpoint, and the README documents running it yourself.independent, abapRead-only security analysis MCP server for SAP audits and SoD review
- KlystronKlystronAn MCP server written in ABAP that runs inside the application server behind an ICF service and exposes typed tools for ABAP development, quality, transports, customizing, roles, Basis operations and integration, with a client-side plugin of SAP skills.independent, abapMCP server in ABAP inside the application server, with agent skills
- pyrfc-mcppyrfc-mcpAn MCP server for calling an SAP system's RFC-enabled function modules from an agent, built so that discovering a function does not authorise calling it: a read or a write is denied unless the function name matches an allowlist the server is configured with.independent, abapMCP server calling RFC-enabled function modules behind an allowlist
- sap-basis-opssap-basis-opsBasis and technical operations at the OS and database layer — the sysadmin plane every other SAP skill set skips — as runbook skills that emit a source-cited command sequence with its pre- and post-checks.independent, abapRunbook skills for Basis operations at the OS and database layer
- abap-offstack-aunitabap-offstack-aunitAn agent skill that bootstraps off-stack ABAP Unit tests and an abaplint gate onto an abapGit-serialised repository, with no live SAP system.independent, abapAgent skill for off-stack ABAP Unit tests and an abaplint gate
- ARC-1 SkillsARC-1 SkillsTwenty-two SKILL.md files shipped in arc-1's own skills/ directory for RAP scaffolding, unit test generation, clean-core audits and migration paths, loaded on demand by description and namespaced /arc-1:<skill> when installed through the Claude Code plugin.independent, abapTwenty-two agent skills shipped with arc-1
- sapskillssapskillsThree agent skills in one repository: Clean ABAP guidance, ADT in VS Code against the Private Cloud Edition, and general guardrails for a coding agent.independent, abapAgent skills for Clean ABAP, ADT in VS Code and guardrails
- SuperClaude for SAPSuperClaude for SAPA Claude Code plugin that installs and wires its own ABAP MCP server, then puts a two-layer write guard between the agent and any system profile marked QA or production.independent, abapClaude Code plugin with an ABAP MCP server and write guards
- claude-abap-devclaude-abap-devFive ABAP workflows for Claude Code — ATC fixing, a clean-core audit, a Clean ABAP review, ABAP Unit and debugging — that ship no system access of their own and run on whichever community MCP servers are already configured.independent, abapFive ABAP workflows for Claude Code on existing MCP servers
- DanKromp/skillOverflowDanKromp/skillOverflowA collection of 41 agent skills, 37 for CAP, Fiori, BTP, UI5, ABAP, Integration Suite and HANA work and four for creating, reviewing and retiring skills, each with its own eval.independent, capCollection of 41 agent skills, 37 for SAP development tasks
- sap-ai-dev-toolkitsap-ai-dev-toolkitAn MCP proxy that pins an agent's ABAP tools to one SAP system: setup reads the BAS destinations or the local SAP GUI landscape and writes one stdio server per system, leaving the credentials in the destination or in the editor's own login prompts.independent, abapMCP proxy pinning an agent's ABAP tools to one SAP system
- sapcli Claude Code Pluginsapcli Claude Code PluginTwo agents and two skills for Claude Code that drive sapcli, the long-standing command line to SAP products, to explore and develop classic ABAP objects rather than speaking ADT directly.independent, abapAgents and skills that drive sapcli instead of ADT
- sap-odata-explorersap-odata-explorerA Rust CLI and desktop app for exploring and testing SAP OData services against real systems — catalog discovery, metadata browsing, an F4 value-help picker and a Fiori-readiness linter — with an agent skill shipped beside it.independent, btpRust CLI and desktop app for exploring SAP OData services
- abap-skillsabap-skillsSeventeen agent skills for ABAP development in one repository: RAP, CDS view entities, ABAP SQL and AMDP, unit testing, authorisation, eventing, abapGit, ABAP Cloud and the clean core levels.independent, abapSeventeen agent skills for ABAP development
- ABAP remote filesystemABAP remote filesystemA Visual Studio Code extension that opens an ABAP system over ADT as an editable filesystem and exposes system tools to GitHub Copilot and external MCP clients.independent, abapVS Code extension editing ABAP systems over ADT as a filesystem
- WerkbankWerkbankA local desktop workspace for AI conversations, attached files and generated HTML Spaces, run by a goose agent and offering a catalog of SAP MCP connectors and ABAP, CAP and UI5 skills.independent, umbrellaLocal desktop workspace for AI conversations with SAP connectors
- claude-abap-skillsclaude-abap-skillsTwo Claude Code plugins of always-on Clean ABAP rules, templates and slash commands — deliberately not an MCP server, and explicit about routing each capability to the server that should answer it.independent, abapClaude Code plugins with Clean ABAP rules and templates
- SAP Power for KiroSAP Power for KiroA set of thirty-five-plus SAP skills packaged as a power for Kiro IDE rather than Claude Code, spanning BTP, UI5, data and analytics, ABAP, CAP and CLI tooling, with automatic context activation in Kiro.independent, btpSAP skills for the Kiro IDE
- SAP Developers MCPSAP Developers MCPThe hosted MCP server of developers.sap.com, over which an agent searches SAP's tutorials, reads missions and queries the site's knowledge graph of tutorials and concepts.sap official, umbrellaHosted MCP server of developers.sap.com tutorials and missions
- secondsky/sap-skillssecondsky/sap-skillsForty SAP development plugins installed through the skills command line or the Claude Code and Codex marketplaces, with a record per plugin of what its claims were checked against.independent, btpForty SAP development plugins for coding agents
- claude-sap-kitclaude-sap-kitA Claude Code plugin of three skills that extends arc-1 with five Custom_* tools for an on-premise ABAP system: running a report and reading its ALV as JSON, previewing a NAST message as PDF, and reading, writing and replacing SmartForms as XML.independent, abapClaude Code plugin adding Custom_* tools to arc-1
- rap-skillsrap-skillsRAP skills delivered branch-per-target, so the playbook you check out only references patterns valid for your deployment — managed on BTP, unmanaged with BAPI wrappers, and S/4HANA Cloud editions planned.independent, abapRAP skills delivered branch-per-target for each deployment
- KlystronKlystronAn MCP server written in ABAP that runs inside the application server behind an ICF service and exposes typed tools for ABAP development, quality, transports, customizing, roles, Basis operations and integration, with a client-side plugin of SAP skills.independent, abapMCP server in ABAP inside the application server, with agent skills
Details
Sources
Sources are graph entities: the same page may back several entries, and an unreachable source flags every row that rests on it.
Compatibility and provenance
History in the atlas
- First seen.
For your agent
- Ref
artifact:abap2ui5-vscode-extension- JSON
- /artifacts/abap2ui5-vscode-extension.json
- MCP
- Connecting an agent