diff --git a/apps/tests/xml-declaration/examples/test-page.xml b/apps/tests/xml-declaration/examples/test-page.xml new file mode 100644 index 000000000..56c40b5e3 --- /dev/null +++ b/apps/tests/xml-declaration/examples/test-page.xml @@ -0,0 +1,5 @@ + + + + diff --git a/apps/tests/xml-declaration/xml-declaration-tests.ts b/apps/tests/xml-declaration/xml-declaration-tests.ts index aa7f93fd9..5e71f8e14 100644 --- a/apps/tests/xml-declaration/xml-declaration-tests.ts +++ b/apps/tests/xml-declaration/xml-declaration-tests.ts @@ -24,6 +24,7 @@ import viewModule = require("ui/core/view"); import platform = require("platform"); import gesturesModule = require("ui/gestures"); import segmentedBar = require("ui/segmented-bar"); +import { Source } from "utils/debug"; export function test_load_IsDefined() { TKUnit.assert(types.isFunction(builder.load), "ui/builder should have load method!"); @@ -974,3 +975,14 @@ export function test_TabViewHasCorrectParentChain() { model.set("testPassed", false); helper.navigateToModuleAndRunTest(("." + moduleName + "/mymodulewithxml/TabViewParentChain"), model, testFunc); } + +export function test_hasSourceCodeLocations() { + var basePath = "xml-declaration/"; + var page = builder.load(__dirname + "/examples/test-page.xml"); + var grid = page.getViewById("grid"); + var gridSource = Source.get(grid); + TKUnit.assertEqual(gridSource.toString(), "file:///app/" + basePath + "examples/test-page.xml:2:3"); + var label = page.getViewById("label"); + var labelSource = Source.get(label); + TKUnit.assertEqual(labelSource.toString(), "file:///app/" + basePath + "examples/test-page.xml:3:5"); +} diff --git a/ui/builder/builder.ts b/ui/builder/builder.ts index b3df917f7..f1c642310 100644 --- a/ui/builder/builder.ts +++ b/ui/builder/builder.ts @@ -44,10 +44,13 @@ function parseInternal(value: string, context: any, uri?: string): ComponentModu var ui: xml2ui.ComponentParser; var errorFormat = (debug && uri) ? xml2ui.SourceErrorFormat(uri) : xml2ui.PositionErrorFormat; + var componentSourceTracker = (debug && uri) ? xml2ui.ComponentSourceTracker(uri) : () => { + // no-op + }; (start = new xml2ui.XmlStringParser(errorFormat)) .pipe(new xml2ui.PlatformFilter()) - .pipe(new xml2ui.XmlStateParser(ui = new xml2ui.ComponentParser(context, errorFormat))); + .pipe(new xml2ui.XmlStateParser(ui = new xml2ui.ComponentParser(context, errorFormat, componentSourceTracker))); start.parse(value); @@ -225,6 +228,19 @@ namespace xml2ui { } } + interface SourceTracker { + (component: any, p: xml.Position): void; + } + + export function ComponentSourceTracker(uri): SourceTracker { + return (component: any, p: xml.Position) => { + if (!Source.get(component)) { + var source = p ? new Source(uri, p.line, p.column) : new Source(uri, -1, -1); + Source.set(component, source); + } + } + } + export class PlatformFilter extends XmlProducerBase implements XmlProducer, XmlConsumer { private currentPlatformContext: string; @@ -293,6 +309,7 @@ namespace xml2ui { elementName: string; templateItems: Array; errorFormat: ErrorFormatter; + sourceTracker: SourceTracker; } /** @@ -380,13 +397,14 @@ namespace xml2ui { if (this._templateProperty.name in this._templateProperty.parent.component) { var context = this._context; var errorFormat = this._templateProperty.errorFormat; + var sourceTracker = this._templateProperty.sourceTracker; var template: Template = () => { var start: xml2ui.XmlArgsReplay; var ui: xml2ui.ComponentParser; (start = new xml2ui.XmlArgsReplay(this._recordedXmlStream, errorFormat)) // No platform filter, it has been filtered allready - .pipe(new XmlStateParser(ui = new ComponentParser(context, errorFormat))); + .pipe(new XmlStateParser(ui = new ComponentParser(context, errorFormat, sourceTracker))); start.replay(); @@ -418,11 +436,13 @@ namespace xml2ui { private parents = new Array(); private complexProperties = new Array(); - private error; + private error: ErrorFormatter; + private sourceTracker: SourceTracker; - constructor(context: any, errorFormat: ErrorFormatter) { + constructor(context: any, errorFormat: ErrorFormatter, sourceTracker: SourceTracker) { this.context = context; this.error = errorFormat; + this.sourceTracker = sourceTracker; } public parse(args: xml.ParserEvent): XmlStateConsumer { @@ -450,7 +470,8 @@ namespace xml2ui { name: name, elementName: args.elementName, templateItems: [], - errorFormat: this.error + errorFormat: this.error, + sourceTracker: this.sourceTracker }); } @@ -472,6 +493,7 @@ namespace xml2ui { } if (componentModule) { + this.sourceTracker(componentModule.component, args.position); if (parent) { if (complexProperty) { // Add component to complex property of parent component. diff --git a/ui/core/view-common.ts b/ui/core/view-common.ts index 6be4d7523..d812361aa 100644 --- a/ui/core/view-common.ts +++ b/ui/core/view-common.ts @@ -17,6 +17,7 @@ import * as visualStateConstants from "ui/styling/visual-state-constants"; import * as bindableModule from "ui/core/bindable"; import * as visualStateModule from "../styling/visual-state"; import * as animModule from "ui/animation"; +import { Source } from "utils/debug"; var bindable: typeof bindableModule; function ensureBindable() { @@ -1151,11 +1152,18 @@ export class View extends ProxyObject implements definition.View { } public toString(): string { + var str = this.typeName; if (this.id) { - return this.typeName + `<${this.id}>`; + str += `<${this.id}>`; + } else { + str += `(${this._domId})`; + } + var source = Source.get(this); + if (source) { + str += `@${source};`; } - return this.typeName + `(${this._domId})`; + return str; } public _setNativeViewFrame(nativeView: any, frame: any) { diff --git a/ui/core/view.ios.ts b/ui/core/view.ios.ts index a1948a03f..dd1754b09 100644 --- a/ui/core/view.ios.ts +++ b/ui/core/view.ios.ts @@ -137,7 +137,7 @@ export class View extends viewCommon.View { // flag not set, setMeasuredDimension() was not invoked, we raise // an exception to warn the developer if ((this._privateFlags & PFLAG_MEASURED_DIMENSION_SET) !== PFLAG_MEASURED_DIMENSION_SET) { - throw new Error("onMeasure() did not set the measured dimension by calling setMeasuredDimension()"); + throw new Error("onMeasure() did not set the measured dimension by calling setMeasuredDimension() " + this); } } }