BC Object Range AnalyzerA Visual Studio Code extension for analyzing AL object ID range usage in Business Central projects. It helps you track which object IDs are in use, find available IDs, and detect conflicts when multiple apps share the same ID range. Features
Table of Contents
Supported Object TypesThe extension recognizes all 13 AL object types that require numeric IDs:
Getting Started
ViewsThe extension adds a new icon in the Activity Bar with two tree views: 1. Used Object IDsShows all detected AL objects organized hierarchically:
2. Unused IDs / GapsShows available ID ranges within your configured
IntelliSense ID SuggestionsThe extension provides automatic IntelliSense completions that suggest the next available object ID when you're creating new AL objects. How It Works
Example
You'll see a completion item like:
Select the suggestion to insert the ID directly into your code. Supported Object TypesIntelliSense suggestions work for all 13 object types that require IDs:
Normal Mode vs Shared Mode
In shared mode, the suggested ID is type-specific because different object types can share the same ID number, but the same type + ID combination must be unique across all projects. Edge Cases
CommandsAvailable from the Command Palette (
ConfigurationOpen VS Code Settings (
Default exclude patterns:
Excluding Projects from AnalysisThe extension provides two complementary ways to exclude projects from being analyzed:
excludeFolders Setting (Recommended for Workspace Root Folders)The Why use When you add a folder to your workspace using "Add Folder to Workspace", VS Code treats it as a workspace root. The The Example:
This will exclude any project whose path contains folders named Key characteristics:
Examples of what gets excluded with
excludePatterns Setting (Glob Patterns)The How Exclude Patterns WorkExclude patterns use glob pattern syntax and are applied when the extension:
The patterns are combined with the Glob Pattern Syntax
Common Use Cases1. Excluding Test ProjectsIf you have test or demo projects that you don't want included in the analysis:
This excludes:
2. Excluding Specific Project by NameTo exclude a specific project folder:
3. Excluding Multiple Projects with a PatternIf your test projects follow a naming convention:
This excludes any folder containing:
4. Excluding Top-Level Folders OnlyTo exclude only top-level folders (not nested ones):
5. Excluding by File PatternTo exclude specific AL files:
This excludes:
6. Complex Multi-Root Workspace ScenarioFor a workspace with multiple apps where you want to exclude specific ones: Workspace structure:
Configuration:
Per-Folder Configuration (Multi-Root Workspaces)Since .code-workspace file:
This allows the Troubleshooting Exclude PatternsIssue: Workspace Root Folder Not Being ExcludedProblem: You added a folder to your workspace using "Add Folder to Workspace" and patterns like Solution: Use the
Why this happens: VS Code's The Issue: Project Still Appearing After ExclusionSolution: Try these approaches in order:
Example:
Issue: Too Many Projects ExcludedSolution: Be more specific with your patterns:
Issue: Exclude Pattern Not WorkingChecklist:
VerificationAfter configuring exclude patterns, verify they work correctly:
Performance TipExcluding unnecessary folders improves scan performance, especially for large workspaces:
Setting Scopes Explained
Where to Configure Settings
Example: Workspace Settings (
|
| Aspect | Normal Mode (default) | Shared Range Mode |
|---|---|---|
| Use case | Each app has its own dedicated ID range | Multiple apps share the same ID range (OnPrem) |
| Gap calculation | Per-project | Per-object-type across all projects |
| Same ID in different apps | OK (they have different ranges) | Conflict (shows warning) |
| "Next available ID" | Per project | Per object type across all projects |
When to use Shared Range Mode
Enable bcObjectRange.sharedRangeMode when:
- You have OnPrem licensing that allocates a single ID range to multiple apps
- Multiple apps in your workspace must not use the same object type + ID combination
- You want to see conflicts when two apps accidentally use the same ID for the same object type
Shared Range Mode
When bcObjectRange.sharedRangeMode is enabled, the extension behavior changes to support scenarios where multiple apps share a single ID range.
Unused IDs View (Shared Mode)
Instead of showing gaps per project, gaps are shown per object type:
📁 Shared Range (50000-50119)
├── 📄 Table
│ ├── ⭕ 50005 - 50010 (6 IDs)
│ └── ⭕ 50050 - 50099 (50 IDs)
├── 📄 Page
│ ├── ⭕ 50000 - 50004 (5 IDs)
│ └── ⭕ 50020 - 50099 (80 IDs)
├── 📄 Codeunit
│ └── ⭕ 50000 - 50099 (100 IDs)
└── ... (all 13 object types)
This is because in AL, different object types can share the same ID number. For example:
table 50000 "My Table"andpage 50000 "My Page"can coexist ✅- But
table 50000in App1 andtable 50000in App2 would be a conflict ❌
Conflict Detection
When two projects have objects with the same type AND same ID, the extension shows warnings:
In the Used IDs view:
⚠️ ID Conflicts (2 conflicts)
├── ❌ table 50000 — App1, App2
└── ❌ page 50005 — App1, App3
📁 App1 (15 objects)
├── 📄 Table (3)
│ ├── ⚠️ 50000 Customer Extended ← Warning icon
│ └── ...
Objects involved in conflicts show a warning icon and tooltip explaining the conflict.
Field and Enum Value Conflict Detection
In shared mode, the extension also detects conflicts within tableextension fields and enumextension values. This is crucial for OnPrem scenarios where multiple apps extend the same base table or enum.
Why Field/Value Conflicts Matter
In AL, when multiple apps extend the same base object:
- Tableextension fields: Each field ID within extensions of the same base table must be unique across all apps
- Enumextension values: Each value ID within extensions of the same base enum must be unique across all apps
For example, if App1 and App2 both extend "Customer" table:
- App1:
tableextension 50000 extends "Customer" { fields { field(50100; "Custom Field 1"; Text[50]) } } - App2:
tableextension 50001 extends "Customer" { fields { field(50100; "Custom Field 2"; Text[50]) } }← Conflict!
Both use field ID 50100 in extensions of the same base table — this will cause a runtime conflict.
Field Conflicts View
The extension detects and displays these conflicts in the Used IDs view:
⚠️ Field Conflicts (2 conflicts)
├── ❌ field 50100 in "Customer" extensions
│ ├── 📄 App1.CustomerExt (50000) → "Custom Field 1"
│ └── 📄 App2.CustomerExt (50001) → "Custom Field 2"
└── ❌ field 50200 in "Sales Header" extensions
├── 📄 App1.SalesExt (50010) → "Extra Field"
└── 📄 App3.SalesExt (50011) → "Another Field"
⚠️ Enum Value Conflicts (1 conflict)
└── ❌ value 5 in "Sales Document Type" extensions
├── 📄 App1.DocTypeExt (50000) → "Custom Type 1"
└── 📄 App2.DocTypeExt (50001) → "Custom Type 2"
How It Works
- Field Parsing: The extension parses
fields { }blocks in tables and tableextensions to extract field IDs, names, and data types - Value Parsing: The extension parses
value()declarations in enums and enumextensions to extract value IDs and names - Extends Detection: For tableextensions and enumextensions, the extension detects which base object is being extended (e.g.,
extends "Customer") - Cross-Project Analysis: Fields and values are grouped by
baseObject:fieldIdorbaseEnum:valueId - Conflict Detection: A conflict is flagged when the same ID is used in extensions from different projects that extend the same base object
Supported Declarations
Table/Tableextension fields:
table 50000 "My Table" {
fields {
field(1; "Primary Key"; Code[20]) { }
field(50100; "Custom Field"; Text[100]) { }
}
}
tableextension 50000 "Customer Ext" extends "Customer" {
fields {
field(50100; "Custom Field"; Text[100]) { }
}
}
Enum/Enumextension values:
enum 50000 "My Enum" {
value(0; "None") { }
value(1; "Option A") { }
}
enumextension 50000 "Doc Type Ext" extends "Sales Document Type" {
value(50; "Custom Document") { }
}
Note: Field and enum value conflict detection is only active in Shared Range Mode (
bcObjectRange.sharedRangeMode: true).
Copy Next ID (Shared Mode)
When you run the "Copy Next Available ID" command in shared mode:
- A quick pick menu appears listing all object types
- Each type shows its next available ID
- Select a type to copy that ID to your clipboard
┌─────────────────────────────────────────────────────┐
│ Select object type to get next available ID │
├─────────────────────────────────────────────────────┤
│ Table Next: 50005 │
│ Tableextension Next: 50000 │
│ Page Next: 50000 │
│ Codeunit Next: 50012 │
│ ... │
└─────────────────────────────────────────────────────┘
How It Works
Project Detection
An AL project is identified by the presence of an app.json file. The extension reads:
{
"name": "My Extension",
"idRanges": [
{ "from": 50000, "to": 50099 },
{ "from": 50100, "to": 50199 }
]
}
Both idRanges (array) and legacy idRange (single object) are supported.
Shared Range Calculation
In shared mode, ranges from all projects are merged:
App1: idRanges: [{ from: 50000, to: 50050 }]
App2: idRanges: [{ from: 50025, to: 50099 }]
App3: idRanges: [{ from: 50000, to: 50099 }]
Merged shared range: 50000 - 50099
Overlapping and adjacent ranges are automatically combined.
Comment Handling
The parser correctly ignores commented-out object declarations:
// table 50000 "Commented Out" { } -- Ignored
/*
page 50001 "Also Commented" { } -- Ignored
*/
table 50002 "Real Object" { } -- Detected
Multiple Objects Per File
The extension handles files containing multiple object declarations:
table 50000 "First Table" { }
table 50001 "Second Table" { }
page 50000 "My Page" { }
All three objects will be detected and listed separately.
Examples
Example 1: Single App (Normal Mode)
Workspace structure:
my-extension/
├── app.json ← idRanges: [{ from: 50000, to: 50099 }]
├── Tables/
│ ├── Tab50000.MyTable.al
│ └── Tab50001.OtherTable.al
└── Pages/
└── Pag50000.MyPage.al
Used IDs view:
📁 My Extension (3 objects)
├── 📄 Table (2)
│ ├── 50000 My Table
│ └── 50001 Other Table
└── 📄 Page (1)
└── 50000 My Page
Unused IDs view:
📁 My Extension (97 IDs available)
└── ⭕ 50002 - 50099 (98 IDs)
Example 2: Multiple Apps with Shared Range
Workspace structure:
workspace/
├── core-app/
│ ├── app.json ← idRanges: [{ from: 50000, to: 50119 }]
│ └── Tab50000.Customer.al
├── sales-app/
│ ├── app.json ← idRanges: [{ from: 50000, to: 50119 }]
│ └── Tab50001.SalesHeader.al
└── inventory-app/
├── app.json ← idRanges: [{ from: 50000, to: 50119 }]
└── Tab50002.Item.al
settings.json:
{
"bcObjectRange.sharedRangeMode": true
}
Unused IDs view (shared mode):
📁 Shared Range (50000-50119)
├── 📄 Table
│ └── ⭕ 50003 - 50119 (117 IDs) ← 50000, 50001, 50002 used across apps
├── 📄 Page
│ └── ⭕ 50000 - 50119 (120 IDs) ← No pages yet
└── 📄 Codeunit
└── ⭕ 50000 - 50119 (120 IDs) ← No codeunits yet
Example 3: Conflict Detection
If sales-app accidentally creates table 50000 (same as core-app):
Used IDs view:
⚠️ ID Conflicts (1 conflict)
└── ❌ table 50000 — core-app, sales-app
📁 core-app (1 objects)
└── 📄 Table (1)
└── ⚠️ 50000 Customer ← Conflict warning
📁 sales-app (2 objects)
└── 📄 Table (2)
├── ⚠️ 50000 SalesCustomer ← Conflict warning
└── 50001 SalesHeader
Known Limitations
| Limitation | Description |
|---|---|
| No ID validation | The extension doesn't validate if objects are within the configured range. It only reports what's found. |
| No field ID tracking | Table field IDs and enum value IDs are not tracked, only top-level object IDs. |
| No real-time sync | Changes are detected via file watching, but there may be a brief delay. |
| Objects without IDs | interface, controladdin, profile, pagecustomization, entitlement, and dotnet are not tracked. |
| Extension objects | Extension objects use their own ID namespace; base object IDs are not resolved. |
| Symbol references | The extension only parses local .al files. It doesn't read symbols from .alpackages or dependencies. |
FAQ
Q: Why don't I see any projects?
A: The extension looks for app.json files to identify AL projects. Make sure:
- Your project has a valid
app.jsonfile - The folder is included in your workspace
- The folder isn't excluded by
bcObjectRange.excludeFoldersorbcObjectRange.excludePatterns
Q: How do I exclude a workspace root folder?
A: Use the excludeFolders setting with the folder name:
{
"bcObjectRange.excludeFolders": ["App_FaultyItems"]
}
This is more reliable than glob patterns for workspace root folders. See the Excluding Projects from Analysis section for details.
Q: Why are some objects missing?
A: Check if:
- The object file has a
.alextension - The object declaration follows standard AL syntax
- The object isn't commented out
- The file isn't in an excluded folder (node_modules, .alpackages, etc.)
Q: When should I enable shared range mode?
A: Enable it when multiple apps in your workspace share the same ID range allocation (common with OnPrem licensing). If each app has its own dedicated range, keep it disabled.
Q: How do I resolve conflicts?
A: When you see a conflict:
- Click on the conflict item to open one of the conflicting files
- Change the object ID in one of the apps to an unused ID
- The conflict will disappear after the next refresh
Q: Why can't I set sharedRangeMode in my folder's settings.json?
A: The sharedRangeMode setting has window scope, which means it applies to the entire VS Code window and cannot be set per-folder. This is by design because shared range mode is a workspace-wide concept that affects how all projects are analyzed together.
To configure sharedRangeMode:
- Multi-root workspace: Add it to your
.code-workspacefile under"settings" - Single folder: Add it to your user settings or the folder's
.vscode/settings.json - User-wide: Add it to your VS Code user settings
Q: Can I use this with AL:Go?
A: Yes! The extension works with any AL project structure. It scans based on app.json files and .al file content.
Q: Does this work with the Vjeko AL Object ID Ninja extension?
A: This extension is independent and uses only local file scanning. It doesn't integrate with external ID management services, but it can be used alongside other extensions.
Requirements
- Visual Studio Code 1.74.0 or higher
- AL projects with valid
app.jsonfiles
Release Notes
0.5.0
- NEW:
excludeFolderssetting - Simple folder name exclusion for workspace root folders- Matches folder names anywhere in the path (case-insensitive)
- Ideal for excluding workspace root folders added via "Add Folder to Workspace"
- Simpler alternative to glob patterns for common use cases
- IMPROVED:
excludePatternsnow works for workspace root folders via post-filtering- Uses
minimatchfor reliable glob pattern matching - Patterns are matched against the full absolute path
- Uses
- IMPROVED: Comprehensive documentation for exclusion settings with examples
0.3.0
- NEW: IntelliSense ID Suggestions - Automatic completion suggestions for the next available object ID when typing AL object declarations
- Triggers on space after object type keyword or with manual IntelliSense (
Ctrl+Space) - Works in both normal mode and shared mode
- Supports all 13 object types that require IDs
- Triggers on space after object type keyword or with manual IntelliSense (
- IMPROVED: Extension now activates on
onLanguage:alfor immediate IntelliSense support - IMPROVED: Project detection prefers the most specific project for nested paths
0.2.0
- IMPROVED: Settings now have proper scopes for multi-root workspace support
autoRefresh,autoRefreshDelay,excludePatterns: Resource scope (per-folder)sharedRangeMode: Window scope (workspace-wide)
- IMPROVED: Documentation updated with setting scope explanations
0.1.0
- NEW: Shared Range Mode for multi-app scenarios with shared ID ranges
- NEW: Conflict detection when same type+ID is used across projects
- NEW: Per-object-type gap calculation in shared mode
- NEW: Quick pick for object type selection when copying next ID
0.0.1
- Initial release
- Support for all 13 AL object types with IDs
- Two-view interface (Used IDs, Unused Gaps)
- Auto-refresh with configurable debounce
- Comment-aware parsing
- Multi-project workspace support
Contributing
Found a bug or have a feature request? Please open an issue on the repository.
License
MIT