Skip to content

Commit 44f2750

Browse files
committed
Added language features
1 parent 9528784 commit 44f2750

34 files changed

Lines changed: 8495 additions & 12 deletions

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## [0.0.8] - 2019-07-27
2+
- Added language features:
3+
- Code completion
4+
- Hover information
5+
- Go to definition
6+
17
## [0.0.4] - 2019-07-17
28
- Update :submitForm
39

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Extension features:
4949
* Generates localization code depending on json files.
5050
* Forms & animation made easy.
5151
* Customizable! so developers can add their own properties and modify some features.
52-
52+
* Supports Code completion, hover information, Go to Definition.
5353

5454
# Get Started
5555

package-lock.json

Lines changed: 34 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"displayName": "XML Layout for Flutter",
44
"description": "XML Layout for Flutter. Brings Angular's style to Flutter!",
55
"publisher": "WaseemDev",
6-
"version": "0.0.7",
6+
"version": "0.0.8",
77
"icon": "images/logo.png",
88
"repository": {
99
"type": "github",
@@ -37,8 +37,12 @@
3737
}
3838
]
3939
},
40+
"extensionDependencies": [
41+
"Dart-Code.dart-code"
42+
],
4043
"scripts": {
4144
"vscode:prepublish": "webpack --mode production",
45+
"compile-prod": "webpack --mode production",
4246
"compile": "webpack --mode none",
4347
"watch": "webpack --mode none",
4448
"postinstall": "node ./node_modules/vscode/bin/install",

src/dart/extension/analytics.ts

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
import * as https from "https";
2+
import * as querystring from "querystring";
3+
import { env, Uri, version as codeVersion, workspace } from "vscode";
4+
import { dartCodeExtensionIdentifier, isChromeOS } from "../shared/constants";
5+
import { Logger } from "../shared/interfaces";
6+
import { WorkspaceContext } from "../shared/workspace";
7+
import { config } from "./config";
8+
import { extensionVersion, hasFlutterExtension, isDevExtension } from "./utils";
9+
10+
// Set to true for analytics to be sent to the debug endpoint (non-logging) for validation.
11+
// This is only required for debugging analytics and needn't be sent for standard Dart Code development (dev hits are already filtered with isDevelopment).
12+
const debug = false;
13+
14+
/// Analytics require that we send a value for uid or cid, but when running in the VS Code
15+
// dev host we don't have either.
16+
const sendAnalyticsFromExtensionDevHost = false;
17+
18+
// Machine ID is not set for extension dev host unless the boolean above is set to true (which
19+
// is usually done for testing purposes).
20+
const machineId = env.machineId !== "someValue.machineId"
21+
? env.machineId
22+
: (sendAnalyticsFromExtensionDevHost ? "35009a79-1a05-49d7-dede-dededededede" : undefined);
23+
24+
enum Category {
25+
Extension,
26+
Analyzer,
27+
Debugger,
28+
}
29+
30+
enum EventAction {
31+
Activated,
32+
SdkDetectionFailure,
33+
Deactivated,
34+
Restart,
35+
HotReload,
36+
OpenObservatory,
37+
OpenTimeline,
38+
OpenDevTools,
39+
}
40+
41+
enum TimingVariable {
42+
Startup,
43+
FirstAnalysis,
44+
SessionDuration,
45+
}
46+
47+
export class Analytics {
48+
public sdkVersion?: string;
49+
public flutterSdkVersion?: string;
50+
public analysisServerVersion?: string;
51+
private readonly formatter: string;
52+
private readonly dummyDartFile = Uri.parse("untitled:foo.dart");
53+
private readonly dartConfig = workspace.getConfiguration("", this.dummyDartFile).get("[dart]") as any;
54+
55+
constructor(private readonly logger: Logger, public workspaceContext: WorkspaceContext) {
56+
this.formatter = this.getFormatterSetting();
57+
}
58+
59+
private getFormatterSetting(): string {
60+
try {
61+
// If there are multiple formatters for Dart, the user can select one, so check
62+
// that first so we don't record their formatter being enabled as ours.
63+
const otherDefaultFormatter = this.getAppliedConfig("editor", "defaultFormatter", false);
64+
if (otherDefaultFormatter && otherDefaultFormatter !== dartCodeExtensionIdentifier)
65+
return otherDefaultFormatter;
66+
67+
// If the user has explicitly disabled ours (without having another selected
68+
// then record that).
69+
if (!config.enableSdkFormatter)
70+
return "Disabled";
71+
72+
// Otherwise record as enabled (and whether on-save).
73+
return this.getAppliedConfig("editor", "formatOnSave")
74+
? "Enabled on Save"
75+
: "Enabled";
76+
} catch {
77+
return "Unknown";
78+
}
79+
}
80+
81+
private getAppliedConfig(section: string, key: string, isResourceScoped = true) {
82+
const dartValue = this.dartConfig ? this.dartConfig[`${section}.${key}`] : undefined;
83+
return dartValue !== undefined && dartValue !== null
84+
? dartValue
85+
: workspace.getConfiguration(section, isResourceScoped ? this.dummyDartFile : undefined).get(key);
86+
}
87+
88+
public logExtensionStartup(timeInMS: number) {
89+
this.event(Category.Extension, EventAction.Activated);
90+
this.time(Category.Extension, TimingVariable.Startup, timeInMS);
91+
}
92+
public logExtensionRestart(timeInMS: number) {
93+
this.event(Category.Extension, EventAction.Restart);
94+
this.time(Category.Extension, TimingVariable.Startup, timeInMS);
95+
}
96+
public logExtensionShutdown(): PromiseLike<void> { return this.event(Category.Extension, EventAction.Deactivated); }
97+
public logSdkDetectionFailure() { this.event(Category.Extension, EventAction.SdkDetectionFailure); }
98+
public logAnalyzerError(description: string, fatal: boolean) { this.error("AS: " + description, fatal); }
99+
public logAnalyzerStartupTime(timeInMS: number) { this.time(Category.Analyzer, TimingVariable.Startup, timeInMS); }
100+
public logDebugSessionDuration(timeInMS: number) { this.time(Category.Debugger, TimingVariable.SessionDuration, timeInMS); }
101+
public logAnalyzerFirstAnalysisTime(timeInMS: number) { this.time(Category.Analyzer, TimingVariable.FirstAnalysis, timeInMS); }
102+
public logDebuggerStart(resourceUri: Uri, debuggerType: string, runType: string) {
103+
const customData = {
104+
cd15: debuggerType,
105+
cd16: runType,
106+
};
107+
this.event(Category.Debugger, EventAction.Activated, resourceUri, customData);
108+
}
109+
public logDebuggerRestart() { this.event(Category.Debugger, EventAction.Restart); }
110+
public logDebuggerHotReload() { this.event(Category.Debugger, EventAction.HotReload); }
111+
public logDebuggerOpenObservatory() { this.event(Category.Debugger, EventAction.OpenObservatory); }
112+
public logDebuggerOpenTimeline() { this.event(Category.Debugger, EventAction.OpenTimeline); }
113+
public logDebuggerOpenDevTools() { this.event(Category.Debugger, EventAction.OpenDevTools); }
114+
115+
private event(category: Category, action: EventAction, resourceUri?: Uri, customData?: any): PromiseLike<void> {
116+
const data: any = {
117+
ea: EventAction[action],
118+
ec: Category[category],
119+
t: "event",
120+
};
121+
122+
// Copy custom data over.
123+
Object.assign(data, customData);
124+
125+
// Force a session start if this is extension activation.
126+
if (category === Category.Extension && action === EventAction.Activated)
127+
data.sc = "start";
128+
129+
// Force a session end if this is extension deactivation.
130+
if (category === Category.Extension && action === EventAction.Deactivated)
131+
data.sc = "end";
132+
133+
return this.send(data, resourceUri);
134+
}
135+
136+
private time(category: Category, timingVariable: TimingVariable, timeInMS: number) {
137+
const data: any = {
138+
t: "timing",
139+
utc: Category[category],
140+
utt: Math.round(timeInMS),
141+
utv: TimingVariable[timingVariable],
142+
};
143+
144+
this.send(data);
145+
}
146+
147+
private error(description: string, fatal: boolean) {
148+
const data: any = {
149+
exd: description.split(/[\n\{\/\\]/)[0].substring(0, 150).trim(),
150+
exf: fatal ? 1 : 0,
151+
t: "exception",
152+
};
153+
154+
this.send(data);
155+
}
156+
157+
private async send(customData: any, resourceUri?: Uri): Promise<void> {
158+
if (!machineId || !config.allowAnalytics || process.env.DART_CODE_IS_TEST_RUN)
159+
return;
160+
161+
const data: any = {
162+
aip: 1,
163+
an: "Dart Code",
164+
av: extensionVersion,
165+
cd1: isDevExtension,
166+
cd10: config.showTodos ? "On" : "Off",
167+
// cd11: config.showLintNames ? "On" : "Off",
168+
cd12: this.formatter,
169+
cd13: this.flutterSdkVersion,
170+
cd14: hasFlutterExtension ? "Installed" : "Not Installed",
171+
cd17: this.workspaceContext.hasAnyFlutterProjects
172+
? (config.previewFlutterUiGuides ? (config.previewFlutterUiGuidesCustomTracking ? "On + Custom Tracking" : "On") : "Off")
173+
: null,
174+
cd2: isChromeOS ? `${process.platform} (ChromeOS)` : process.platform,
175+
cd3: this.sdkVersion,
176+
cd4: this.analysisServerVersion,
177+
cd5: codeVersion,
178+
cd6: resourceUri ? this.getDebuggerPreference(resourceUri) : null,
179+
cd7: this.workspaceContext.workspaceTypeDescription,
180+
cd8: config.closingLabels ? "On" : "Off",
181+
cd9: this.workspaceContext.hasAnyFlutterProjects ? (config.flutterHotReloadOnSave ? "On" : "Off") : null,
182+
// TODO: Auto-save
183+
// TODO: Hot-restart-on-save
184+
cid: machineId,
185+
tid: "UA-2201586-19",
186+
ul: env.language,
187+
v: "1", // API Version.
188+
};
189+
190+
// Copy custom data over.
191+
Object.assign(data, customData);
192+
193+
if (debug)
194+
this.logger.info("Sending analytic: " + JSON.stringify(data));
195+
196+
const options: https.RequestOptions = {
197+
headers: {
198+
"Content-Type": "application/x-www-form-urlencoded",
199+
},
200+
hostname: "www.google-analytics.com",
201+
method: "POST",
202+
path: debug ? "/debug/collect" : "/collect",
203+
port: 443,
204+
};
205+
206+
await new Promise((resolve) => {
207+
try {
208+
const req = https.request(options, (resp) => {
209+
if (debug)
210+
resp.on("data", (c) => {
211+
try {
212+
const gaDebugResp = JSON.parse(c.toString());
213+
if (gaDebugResp && gaDebugResp.hitParsingResult && gaDebugResp.hitParsingResult[0].valid === true)
214+
this.logger.info("Sent OK!");
215+
else if (gaDebugResp && gaDebugResp.hitParsingResult && gaDebugResp.hitParsingResult[0].valid === false)
216+
this.logger.warn(c.toString());
217+
else
218+
this.logger.warn("Unexpected GA debug response: " + c.toString());
219+
} catch (e) {
220+
this.logger.warn("Error in GA debug response: " + c.toString());
221+
}
222+
});
223+
224+
if (!resp || !resp.statusCode || resp.statusCode < 200 || resp.statusCode > 300) {
225+
this.logger.info(`Failed to send analytics ${resp && resp.statusCode}: ${resp && resp.statusMessage}`);
226+
}
227+
resolve();
228+
});
229+
req.write(querystring.stringify(data));
230+
req.end();
231+
} catch (e) {
232+
this.logger.error(`Failed to send analytics: ${e}`);
233+
resolve();
234+
}
235+
});
236+
}
237+
238+
private getDebuggerPreference(resourceUri: Uri): string {
239+
const conf = config.for(resourceUri);
240+
if (conf.debugSdkLibraries && conf.debugExternalLibraries)
241+
return "All code";
242+
else if (conf.debugSdkLibraries)
243+
return "My code + SDK";
244+
else if (conf.debugExternalLibraries)
245+
return "My code + Libraries";
246+
else
247+
return "My code";
248+
}
249+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import * as child_process from "child_process";
2+
import * as vs from "vscode";
3+
4+
const channels: { [key: string]: vs.OutputChannel } = {};
5+
6+
export function createChannel(name: string): vs.OutputChannel {
7+
if (!channels[name])
8+
channels[name] = vs.window.createOutputChannel(name);
9+
10+
return channels[name];
11+
}
12+
13+
export function getChannel(name: string): vs.OutputChannel {
14+
if (!channels[name])
15+
return createChannel(name);
16+
17+
return channels[name];
18+
}
19+
20+
export function runProcessInChannel(process: child_process.ChildProcess, channel: vs.OutputChannel) {
21+
process.stdout.on("data", (data) => channel.append(data.toString()));
22+
process.stderr.on("data", (data) => channel.append(data.toString()));
23+
process.on("close", (code) => channel.appendLine(`exit code ${code}`));
24+
}

0 commit comments

Comments
 (0)