Skip to content

Commit ce48e3f

Browse files
testforstephenfbricon
authored andcommitted
Auto fill file contents when new a Java file is created
Signed-off-by: Jinbo Wang <jinbwan@microsoft.com>
1 parent 8445e03 commit ce48e3f

3 files changed

Lines changed: 127 additions & 1 deletion

File tree

src/buildpath.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ interface SourcePath {
1515
projectType: string;
1616
}
1717

18-
interface ListCommandResult extends Result {
18+
export interface ListCommandResult extends Result {
1919
data?: SourcePath[];
2020
}
2121

src/extension.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { serverTaskPresenter } from './serverTaskPresenter';
3131
import { serverStatus, ServerStatusKind } from './serverStatus';
3232
import { SyntaxLanguageClient } from './syntaxLanguageClient';
3333
import { registerClientProviders, ClientHoverProvider } from './providerDispatcher';
34+
import * as fileEventHandler from './fileEventHandler';
3435

3536
let languageClient: LanguageClient;
3637
const syntaxClient: SyntaxLanguageClient = new SyntaxLanguageClient();
@@ -298,6 +299,7 @@ export function activate(context: ExtensionContext): Promise<ExtensionAPI> {
298299
onDidClasspathUpdate
299300
});
300301
snippetProvider.setActivation(false);
302+
fileEventHandler.setServerStatus(true);
301303
break;
302304
case 'Error':
303305
serverStatus.updateServerStatus(ServerStatusKind.Error);
@@ -312,6 +314,7 @@ export function activate(context: ExtensionContext): Promise<ExtensionAPI> {
312314
isTestFile,
313315
onDidClasspathUpdate
314316
});
317+
fileEventHandler.setServerStatus(true);
315318
break;
316319
case 'Starting':
317320
case 'Message':
@@ -491,6 +494,7 @@ export function activate(context: ExtensionContext): Promise<ExtensionAPI> {
491494
}
492495
}
493496

497+
fileEventHandler.registerFileEventHandlers(languageClient, context);
494498
if (requireSyntaxServer) {
495499
syntaxClient.start();
496500
}
@@ -499,6 +503,7 @@ export function activate(context: ExtensionContext): Promise<ExtensionAPI> {
499503
context.subscriptions.push(commands.registerCommand(Commands.OPEN_OUTPUT, () => languageClient.outputChannel.show(ViewColumn.Three)));
500504
context.subscriptions.push(commands.registerCommand(Commands.SHOW_SERVER_TASK_STATUS, () => serverTaskPresenter.presentServerTaskView()));
501505
}
506+
502507
// Register commands here to make it available even when the language client fails
503508
context.subscriptions.push(commands.registerCommand(Commands.OPEN_SERVER_LOG, (column: ViewColumn) => openServerLogFile(workspacePath, column)));
504509

src/fileEventHandler.ts

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
'use strict';
2+
3+
import * as path from 'path';
4+
import { workspace, FileCreateEvent, ExtensionContext, FileRenameEvent, window, TextDocument, SnippetString, commands, Uri } from 'vscode';
5+
import { LanguageClient } from 'vscode-languageclient';
6+
import { ListCommandResult } from './buildpath';
7+
import { Commands } from './commands';
8+
9+
let serverReady: boolean = false;
10+
11+
export function setServerStatus(ready: boolean) {
12+
serverReady = ready;
13+
}
14+
15+
export function registerFileEventHandlers(client: LanguageClient, context: ExtensionContext, ) {
16+
context.subscriptions.push(workspace.onDidCreateFiles(handleNewJavaFiles));
17+
}
18+
19+
async function handleNewJavaFiles(e: FileCreateEvent) {
20+
const emptyFiles: Uri[] = [];
21+
const textDocuments: TextDocument[] = [];
22+
for (const uri of e.files) {
23+
if (!uri.fsPath || !uri.fsPath.endsWith(".java")) {
24+
continue;
25+
}
26+
27+
const textDocument = await workspace.openTextDocument(uri);
28+
if (textDocument.getText()) { // ignore the non-empty files
29+
continue;
30+
}
31+
32+
emptyFiles.push(uri);
33+
textDocuments.push(textDocument);
34+
}
35+
36+
if (!emptyFiles.length) {
37+
return;
38+
}
39+
40+
let sourcePaths: string[] = [];
41+
if (serverReady) {
42+
const result: ListCommandResult = await commands.executeCommand<ListCommandResult>(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.LIST_SOURCEPATHS);
43+
if (result && result.data && result.data.length) {
44+
sourcePaths = result.data.map((sourcePath) => sourcePath.path).sort((a, b) => b.length - a.length);
45+
}
46+
}
47+
48+
for (let i = 0; i < emptyFiles.length; i++) {
49+
const packageName = resolvePackageName(sourcePaths, emptyFiles[i].fsPath);
50+
const typeName: string = resolveTypeName(textDocuments[i].fileName);
51+
const snippets: string[] = [];
52+
if (packageName) {
53+
snippets.push(`package ${packageName};`);
54+
}
55+
snippets.push("");
56+
if (!serverReady || await isVersionLessThan(emptyFiles[i].toString(), 14)) {
57+
snippets.push(`public \${1|class,interface,enum|} ${typeName} {`);
58+
} else {
59+
snippets.push(`public \${1|class ${typeName},interface ${typeName},enum ${typeName},record ${typeName}()|} {`);
60+
}
61+
snippets.push("");
62+
snippets.push("}");
63+
const textEditor = await window.showTextDocument(textDocuments[i]);
64+
textEditor.insertSnippet(new SnippetString(snippets.join("\n")));
65+
}
66+
}
67+
68+
function resolveTypeName(filePath: string): string {
69+
const fileName: string = path.basename(filePath);
70+
const extName: string = path.extname(fileName);
71+
return fileName.substring(0, fileName.length - extName.length);
72+
}
73+
74+
function resolvePackageName(sourcePaths: string[], filePath: string): string {
75+
if (!sourcePaths || !sourcePaths.length) {
76+
return "";
77+
}
78+
79+
for (const sourcePath of sourcePaths) {
80+
if (isPrefix(sourcePath, filePath)) {
81+
const relative = path.relative(sourcePath, path.dirname(filePath));
82+
return relative.replace(/[\/\\]/g, ".");
83+
}
84+
}
85+
86+
return "";
87+
}
88+
89+
function isPrefix(parentPath: string, filePath: string): boolean {
90+
const relative = path.relative(parentPath, filePath);
91+
return relative && !relative.startsWith('..') && !path.isAbsolute(relative);
92+
}
93+
94+
const COMPLIANCE = "org.eclipse.jdt.core.compiler.compliance";
95+
async function isVersionLessThan(fileUri: string, targetVersion: number): Promise<boolean> {
96+
let projectSettings = {};
97+
try {
98+
projectSettings = await commands.executeCommand<Object>(
99+
Commands.EXECUTE_WORKSPACE_COMMAND, Commands.GET_PROJECT_SETTINGS, fileUri, [ COMPLIANCE ]);
100+
} catch (err) {
101+
// do nothing.
102+
}
103+
104+
let javaVersion = 0;
105+
let complianceVersion = projectSettings[COMPLIANCE];
106+
if (complianceVersion) {
107+
// Ignore '1.' prefix for legacy Java versions
108+
if (complianceVersion.startsWith('1.')) {
109+
complianceVersion = complianceVersion.substring(2);
110+
}
111+
112+
// look into the interesting bits now
113+
const regexp = /\d+/g;
114+
const match = regexp.exec(complianceVersion);
115+
if (match) {
116+
javaVersion = parseInt(match[0]);
117+
}
118+
}
119+
120+
return javaVersion < targetVersion;
121+
}

0 commit comments

Comments
 (0)