Skip to content

Commit 6bfc715

Browse files
leftstickphilwebb
authored andcommittedFeb 15, 2023
Create Visual Studio Code extension
Add Visual Studio Code extension to apply spring-javaformat conventions. See gh-142
·
v0.0.47v0.0.36
1 parent c7fa27d commit 6bfc715

31 files changed

+1224
-0
lines changed
 

‎.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,11 @@ spring-javaformat/spring-javaformat-formatter-eclipse-rewriter/bin
2323
build.log
2424
pid
2525
.factorypath
26+
27+
# npm
28+
node_modules/
29+
30+
# vscode
31+
spring-javaformat-vscode/spring-javaformat/out/
32+
spring-javaformat-vscode/spring-javaformat/runtime/
33+
*.vsix

‎README.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,12 @@ Once the configuration file is created, configure your IDE to use it:
268268

269269

270270

271+
=== Visual Studio Code
272+
The vscode extension provides custom formatter support for Visual Studio Code.
273+
The extension is automatically activated whenever a `.java` file is opened. And it requires a few seconds to warm-up while you start with the first workspace.
274+
275+
276+
271277
=== About the Conventions
272278
Most of the coding conventions and style comes from the Spring Framework and Spring Boot projects.
273279
Spring Framework manually formats code, where as Spring Boot uses automatic formatting.

‎spring-javaformat-vscode/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# spring-javaformat-vscode
2+
3+
`spring-javaformat` extension for visual studio code.
4+
5+
![](./format.gif)
6+
7+
## Prerequisites
8+
9+
* Install [node.js](https://nodejs.org/en/download/)
10+
* Install [yarn](https://yarnpkg.com/en/docs/install)
11+
* Install [vsce](https://code.visualstudio.com/api/working-with-extensions/publishing-extension#vsce)
12+
13+
## Generate extension
14+
15+
Just `mvn clean package`
16+
17+
18+
> `spring-javaformat-1.0.0.vsix` will be generated there
19+

‎spring-javaformat-vscode/format.gif

1.75 MB
Loading

‎spring-javaformat-vscode/pom.xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>io.spring.javaformat</groupId>
8+
<artifactId>spring-javaformat-build</artifactId>
9+
<version>0.0.36-SNAPSHOT</version>
10+
</parent>
11+
<artifactId>spring-javaformat-vscode</artifactId>
12+
<packaging>pom</packaging>
13+
<name>Spring JavaFormat Visual Studio Code</name>
14+
<properties>
15+
<main.basedir>${basedir}/..</main.basedir>
16+
</properties>
17+
<modules>
18+
<module>spring-javaformat-format-service</module>
19+
</modules>
20+
</project>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"configurations": [
3+
{
4+
"type": "java",
5+
"name": "CodeLens (Launch) - FormatterWebApplication",
6+
"request": "launch",
7+
"mainClass": "io.spring.format.FormatterWebApplication",
8+
"projectName": "spring-javaformat-format-service"
9+
}
10+
]
11+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"editor.formatOnSave": true,
3+
"java.format.enabled": false
4+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<groupId>io.spring.javaformat</groupId>
7+
<artifactId>spring-javaformat-vscode</artifactId>
8+
<version>0.0.36-SNAPSHOT</version>
9+
</parent>
10+
<artifactId>spring-javaformat-format-service</artifactId>
11+
<packaging>jar</packaging>
12+
<name>Spring JavaFormat format-service</name>
13+
<properties>
14+
<main.basedir>${basedir}/../..</main.basedir>
15+
</properties>
16+
<build>
17+
<plugins>
18+
<plugin>
19+
<groupId>org.springframework.boot</groupId>
20+
<artifactId>spring-boot-maven-plugin</artifactId>
21+
<version>2.1.8.RELEASE</version>
22+
<configuration>
23+
<outputDirectory>${main.basedir}/spring-javaformat-vscode/spring-javaformat/runtime/</outputDirectory>
24+
</configuration>
25+
<executions>
26+
<execution>
27+
<goals>
28+
<goal>repackage</goal>
29+
</goals>
30+
</execution>
31+
</executions>
32+
</plugin>
33+
<plugin>
34+
<groupId>org.codehaus.mojo</groupId>
35+
<artifactId>exec-maven-plugin</artifactId>
36+
<configuration>
37+
<workingDirectory>${main.basedir}/spring-javaformat-vscode/spring-javaformat</workingDirectory>
38+
</configuration>
39+
<executions>
40+
<execution>
41+
<id>exec-yarn-install</id>
42+
<phase>compile</phase>
43+
<configuration>
44+
<executable>yarn</executable>
45+
</configuration>
46+
<goals>
47+
<goal>exec</goal>
48+
</goals>
49+
</execution>
50+
<execution>
51+
<id>exec-package</id>
52+
<phase>package</phase>
53+
<configuration>
54+
<executable>vsce</executable>
55+
<arguments>
56+
<argument>package</argument>
57+
</arguments>
58+
</configuration>
59+
<goals>
60+
<goal>exec</goal>
61+
</goals>
62+
</execution>
63+
</executions>
64+
</plugin>
65+
<plugin>
66+
<groupId>org.apache.maven.plugins</groupId>
67+
<artifactId>maven-checkstyle-plugin</artifactId>
68+
<executions>
69+
<execution>
70+
<id>checkstyle-validation</id>
71+
<phase>validate</phase>
72+
<configuration>
73+
<excludes>**/*/FormatterWebApplication.java</excludes>
74+
</configuration>
75+
</execution>
76+
</executions>
77+
</plugin>
78+
<plugin>
79+
<groupId>io.spring.javaformat</groupId>
80+
<artifactId>spring-javaformat-maven-plugin</artifactId>
81+
<version>0.0.16-SNAPSHOT</version>
82+
</plugin>
83+
</plugins>
84+
</build>
85+
<dependencyManagement>
86+
<dependencies>
87+
<dependency>
88+
<groupId>org.springframework.boot</groupId>
89+
<artifactId>spring-boot-dependencies</artifactId>
90+
<version>2.1.6.RELEASE</version>
91+
<type>pom</type>
92+
<scope>import</scope>
93+
</dependency>
94+
</dependencies>
95+
</dependencyManagement>
96+
<dependencies>
97+
<!-- Compile -->
98+
<dependency>
99+
<groupId>org.springframework.boot</groupId>
100+
<artifactId>spring-boot-starter</artifactId>
101+
<version>2.1.6.RELEASE</version>
102+
</dependency>
103+
<dependency>
104+
<groupId>org.springframework.boot</groupId>
105+
<artifactId>spring-boot-starter-web</artifactId>
106+
<version>2.1.6.RELEASE</version>
107+
</dependency>
108+
<dependency>
109+
<groupId>io.spring.javaformat</groupId>
110+
<artifactId>spring-javaformat-formatter</artifactId>
111+
<version>${project.version}</version>
112+
</dependency>
113+
<!-- Provided -->
114+
<dependency>
115+
<groupId>io.spring.javaformat</groupId>
116+
<artifactId>spring-javaformat-formatter-eclipse-runtime</artifactId>
117+
<version>${project.version}</version>
118+
<scope>provided</scope>
119+
</dependency>
120+
</dependencies>
121+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2017-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.format;
18+
19+
import org.springframework.boot.SpringApplication;
20+
import org.springframework.boot.autoconfigure.SpringBootApplication;
21+
import org.springframework.scheduling.annotation.EnableScheduling;
22+
23+
@EnableScheduling
24+
@SpringBootApplication
25+
public class FormatterWebApplication {
26+
27+
public FormatterWebApplication() {
28+
29+
}
30+
31+
public static void main(String[] args) {
32+
SpringApplication.run(FormatterWebApplication.class, args);
33+
}
34+
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2017-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.format.controllers;
18+
19+
import org.springframework.web.bind.annotation.RequestBody;
20+
import org.springframework.web.bind.annotation.RequestMapping;
21+
import org.springframework.web.bind.annotation.RequestMethod;
22+
import org.springframework.web.bind.annotation.RestController;
23+
24+
import io.spring.format.request.FormatRequest;
25+
import io.spring.format.tools.FormatContent;
26+
27+
/**
28+
* .
29+
*
30+
* @author Howard Zuo
31+
*/
32+
@RestController
33+
public class FormatController {
34+
35+
@RequestMapping(method = RequestMethod.POST, value = "/format/code")
36+
String formatSource(@RequestBody FormatRequest req) {
37+
38+
return FormatContent.formatContent(req.getSource());
39+
40+
}
41+
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2017-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.format.controllers;
18+
19+
import org.springframework.beans.factory.annotation.Autowired;
20+
import org.springframework.web.bind.annotation.RequestMapping;
21+
import org.springframework.web.bind.annotation.RequestMethod;
22+
import org.springframework.web.bind.annotation.RestController;
23+
24+
import io.spring.format.services.HealthService;
25+
26+
/**
27+
* .
28+
*
29+
* @author Howard Zuo
30+
*/
31+
@RestController
32+
public class HealthController {
33+
34+
@Autowired
35+
private HealthService service;
36+
37+
@RequestMapping(method = RequestMethod.GET, value = "/health")
38+
String heartbeat() {
39+
this.service.setLastHeartbeat(System.currentTimeMillis());
40+
return "";
41+
}
42+
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2017-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.format.request;
18+
19+
/**
20+
* .
21+
*
22+
* @author Howard Zuo
23+
*/
24+
public class FormatRequest {
25+
26+
private String source = "";
27+
28+
public void setSource(String source) {
29+
this.source = source;
30+
}
31+
32+
public String getSource() {
33+
return this.source;
34+
}
35+
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2017-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.format.services;
18+
19+
import org.springframework.scheduling.annotation.Scheduled;
20+
import org.springframework.stereotype.Service;
21+
22+
/**
23+
* .
24+
*
25+
* @author Howard Zuo
26+
*/
27+
@Service
28+
public class HealthService {
29+
30+
private Long lastHeartbeat;
31+
32+
public Long getLastHeartbeat() {
33+
return this.lastHeartbeat;
34+
}
35+
36+
public void setLastHeartbeat(Long lastHeartbeat) {
37+
this.lastHeartbeat = lastHeartbeat;
38+
}
39+
40+
@Scheduled(fixedRate = 1000 * 60 * 5)
41+
public void liveCheck() {
42+
if (this.lastHeartbeat == null) {
43+
return;
44+
}
45+
if ((System.currentTimeMillis() - this.lastHeartbeat) > (1000 * 60 * 5)) {
46+
System.exit(0);
47+
}
48+
}
49+
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2017-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.format.tools;
18+
19+
import java.util.regex.Pattern;
20+
21+
import org.eclipse.jface.text.Document;
22+
import org.eclipse.jface.text.IDocument;
23+
import org.eclipse.text.edits.TextEdit;
24+
25+
import io.spring.javaformat.formatter.Formatter;
26+
27+
/**
28+
* . Format specified java content
29+
*
30+
* @author Howard Zuo
31+
*/
32+
final public class FormatContent {
33+
34+
private static final Pattern TRAILING_WHITESPACE = Pattern.compile(" +$", Pattern.MULTILINE);
35+
36+
private FormatContent() {
37+
38+
}
39+
40+
public static String formatContent(String source) {
41+
if (source == null || "".equals(source)) {
42+
return "";
43+
}
44+
45+
try {
46+
final Formatter formatter = new Formatter();
47+
TextEdit textEdit = formatter.format(source);
48+
IDocument document = new Document(source);
49+
textEdit.apply(document);
50+
String formattedContent = document.get();
51+
return trimTrailingWhitespace(formattedContent);
52+
}
53+
catch (Exception e) {
54+
return source;
55+
}
56+
}
57+
58+
private static String trimTrailingWhitespace(String content) {
59+
return TRAILING_WHITESPACE.matcher(content).replaceAll("");
60+
}
61+
62+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# 基础应用配置
2+
spring:
3+
application.name: Spring-Java-Formatter-Web
4+
server:
5+
port: ${port:9987}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"tabWidth": 2,
3+
"semi": false,
4+
"singleQuote": true,
5+
"printWidth": 110
6+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
// See http://go.microsoft.com/fwlink/?LinkId=827846
3+
// for the documentation about the extensions.json format
4+
"recommendations": [
5+
"ms-vscode.vscode-typescript-tslint-plugin"
6+
]
7+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// A launch configuration that compiles the extension and then opens it inside a new window
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
{
6+
"version": "0.2.0",
7+
"configurations": [
8+
{
9+
"name": "Run Extension",
10+
"type": "extensionHost",
11+
"request": "launch",
12+
"runtimeExecutable": "${execPath}",
13+
"args": [
14+
"--extensionDevelopmentPath=${workspaceFolder}"
15+
],
16+
"outFiles": [
17+
"${workspaceFolder}/out/**/*.js"
18+
],
19+
"preLaunchTask": "npm: watch"
20+
},
21+
{
22+
"name": "Extension Tests",
23+
"type": "extensionHost",
24+
"request": "launch",
25+
"runtimeExecutable": "${execPath}",
26+
"args": [
27+
"--extensionDevelopmentPath=${workspaceFolder}",
28+
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
29+
],
30+
"outFiles": [
31+
"${workspaceFolder}/out/test/**/*.js"
32+
],
33+
"preLaunchTask": "npm: watch"
34+
}
35+
]
36+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Place your settings in this file to overwrite default and user settings.
2+
{
3+
"files.exclude": {
4+
"out": false // set this to true to hide the "out" folder with the compiled JS files
5+
},
6+
"search.exclude": {
7+
"out": true // set this to false to include "out" folder in search results
8+
},
9+
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
10+
"typescript.tsc.autoDetect": "off",
11+
"[typescript]": {
12+
"editor.defaultFormatter": "esbenp.prettier-vscode"
13+
},
14+
"editor.formatOnSave": true
15+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// See https://go.microsoft.com/fwlink/?LinkId=733558
2+
// for the documentation about the tasks.json format
3+
{
4+
"version": "2.0.0",
5+
"tasks": [
6+
{
7+
"type": "npm",
8+
"script": "watch",
9+
"problemMatcher": "$tsc-watch",
10+
"isBackground": true,
11+
"presentation": {
12+
"reveal": "never"
13+
},
14+
"group": {
15+
"kind": "build",
16+
"isDefault": true
17+
}
18+
}
19+
]
20+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.vscode/**
2+
.vscode-test/**
3+
out/test/**
4+
src/**
5+
.gitignore
6+
vsc-extension-quickstart.md
7+
**/tsconfig.json
8+
**/tslint.json
9+
**/*.map
10+
**/*.ts
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# spring-javaformat
2+
3+
> Format .java files in spring-javaformat way
4+
5+
6+
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"name": "spring-javaformat",
3+
"displayName": "spring-javaformat-vscode-extension",
4+
"description": "Format .java files in spring-javaformat way",
5+
"version": "1.0.0",
6+
"publisher": "howardzuo",
7+
"engines": {
8+
"vscode": "^1.36.0"
9+
},
10+
"repository": {
11+
"type": "git",
12+
"url": "https://github.com/spring-io/spring-javaformat.git"
13+
},
14+
"scripts": {
15+
"vscode:prepublish": "yarn run compile",
16+
"compile": "tsc -p ./",
17+
"watch": "tsc -watch -p ./"
18+
},
19+
"categories": [
20+
"Formatters"
21+
],
22+
"activationEvents": [
23+
"onLanguage:java",
24+
"onLanguage:markdown"
25+
],
26+
"main": "./out/extension.js",
27+
"devDependencies": {
28+
"@types/glob": "^7.1.1",
29+
"@types/markdown-it": "^0.0.8",
30+
"@types/node": "^10.12.21",
31+
"@types/vscode": "^1.36.0",
32+
"glob": "^7.1.4",
33+
"tslint": "^5.18.0",
34+
"typescript": "^3.5.3"
35+
},
36+
"dependencies": {
37+
"axios": "^0.19.0",
38+
"markdown-it": "^10.0.0",
39+
"portfinder": "^1.0.21",
40+
"ps-list": "^6.3.0"
41+
}
42+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import {
2+
DocumentFormattingEditProvider,
3+
TextDocument,
4+
FormattingOptions,
5+
CancellationToken,
6+
ProviderResult,
7+
TextEdit,
8+
Range
9+
} from 'vscode'
10+
11+
import { formatMarkdown } from './formatters/MarkdownFormatter'
12+
import { formatJava } from './formatters/JavaFormatter'
13+
14+
export default class SpringJavaFormatter implements DocumentFormattingEditProvider {
15+
provideDocumentFormattingEdits(
16+
document: TextDocument,
17+
options: FormattingOptions,
18+
token: CancellationToken
19+
): ProviderResult<TextEdit[]> {
20+
if (document.languageId === 'java') {
21+
return formatJava(document)
22+
}
23+
if (document.languageId === 'markdown') {
24+
return formatMarkdown(document)
25+
}
26+
return []
27+
}
28+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// The module 'vscode' contains the VS Code extensibility API
2+
// Import the module and reference it with the alias vscode in your code below
3+
import * as vscode from 'vscode'
4+
import SpringJavaFormatter from './SpringJavaFormatter'
5+
6+
// this method is called when your extension is activated
7+
// your extension is activated the very first time the command is executed
8+
export function activate(context: vscode.ExtensionContext) {
9+
// Use the console to output diagnostic information (console.log) and errors (console.error)
10+
// This line of code will only be executed once when your extension is activated
11+
console.log('Congratulations, your extension "spring-javaformat" is now active!')
12+
13+
context.subscriptions.push(
14+
vscode.languages.registerDocumentFormattingEditProvider(
15+
[
16+
{
17+
language: 'java',
18+
scheme: 'file'
19+
},
20+
{
21+
language: 'markdown',
22+
scheme: 'file'
23+
}
24+
],
25+
new SpringJavaFormatter()
26+
)
27+
)
28+
}
29+
30+
// this method is called when your extension is deactivated
31+
export function deactivate() {}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { TextDocument, Range, TextEdit } from 'vscode'
2+
3+
import FormatService from '../services/FormatService'
4+
5+
export async function formatJava(document: TextDocument): Promise<Array<TextEdit>> {
6+
const code = await FormatService.getInstance().formatCode(document.getText())
7+
8+
const range = new Range(document.positionAt(0), document.positionAt(document.getText().length))
9+
return [TextEdit.replace(range, code)]
10+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { TextDocument, Range, TextEdit } from 'vscode'
2+
3+
import FormatService from '../services/FormatService'
4+
import MarkdownIt from 'markdown-it'
5+
import Token from 'markdown-it/lib/token'
6+
7+
const md = new MarkdownIt()
8+
9+
export async function formatMarkdown(document: TextDocument): Promise<Array<TextEdit>> {
10+
const source = document.getText()
11+
const tokens = md.parse(source, {})
12+
13+
const editsPromise = tokens
14+
.filter((t): t is Token => t.type === 'fence' && t.tag === 'code' && t.info === 'java')
15+
.map(async token => {
16+
const startIndex = source.indexOf(token.content)
17+
const code = await FormatService.getInstance().formatCode(token.content)
18+
19+
const range = new Range(
20+
document.positionAt(startIndex),
21+
document.positionAt(startIndex + token.content.length)
22+
)
23+
24+
return TextEdit.replace(range, code)
25+
})
26+
27+
return Promise.all(editsPromise)
28+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import * as child_process from 'child_process'
2+
import * as portfinder from 'portfinder'
3+
import psList from 'ps-list'
4+
import axios from 'axios'
5+
import { resolve } from 'path'
6+
import { window } from 'vscode'
7+
8+
const JAR_NAME = 'spring-javaformat-format-service-0.0.16-SNAPSHOT.jar'
9+
10+
const RUNTIME_JAR_PATH = resolve(__dirname, '..', '..', 'runtime', JAR_NAME)
11+
12+
export default class FormatService {
13+
private static instance: FormatService = new FormatService()
14+
private port: number = 9987
15+
16+
constructor() {
17+
const hideFunc = window.setStatusBarMessage('spring-javaformat service initializing....')
18+
this.init()
19+
.catch(err => {
20+
console.error('ERROR:', err)
21+
})
22+
.finally(() => {
23+
hideFunc.dispose()
24+
})
25+
}
26+
27+
private async init() {
28+
this.startNextScheduledJob()
29+
const proc = await this.getJavaFormatServiceProcessInfo()
30+
if (proc) {
31+
const matched = (proc.cmd || '').match(/-Dport=([0-9]+)\s/)
32+
if (matched) {
33+
this.port = +matched[1]
34+
}
35+
console.log('spring-javaformat service is running with other workspace, no need run again')
36+
return
37+
}
38+
const port = await this.findAvailablePort()
39+
this.port = port
40+
await this.run(port)
41+
console.log('spring-javaformat service running')
42+
}
43+
44+
private startNextScheduledJob() {
45+
setTimeout(async () => {
46+
const proc = await this.getJavaFormatServiceProcessInfo()
47+
if (!proc) {
48+
const port = await this.findAvailablePort()
49+
this.port = port
50+
await this.run(port)
51+
}
52+
await this.sendHeartbeat()
53+
54+
this.startNextScheduledJob()
55+
}, 1000 * 60)
56+
}
57+
58+
private async getJavaFormatServiceProcessInfo() {
59+
const list = await psList()
60+
return list.find(l => (l.cmd || '').includes(JAR_NAME))
61+
}
62+
63+
private async findAvailablePort() {
64+
const port = await portfinder.getPortPromise({
65+
port: 20000,
66+
stopPort: 60000
67+
})
68+
return port
69+
}
70+
71+
private async run(port: number) {
72+
return new Promise((resolve, reject) => {
73+
const childProcess = child_process.exec(`java -Dport=${port} -jar ${RUNTIME_JAR_PATH} `, {})
74+
75+
childProcess.stdout.on('data', data => {
76+
if (data.includes('Started FormatterWebApplication')) {
77+
resolve()
78+
}
79+
})
80+
81+
childProcess.on('error', reject)
82+
})
83+
}
84+
85+
async formatCode(source: string): Promise<string> {
86+
try {
87+
const result = await axios.post(`http://localhost:${this.port}/format/code`, {
88+
source
89+
})
90+
return result.data
91+
} catch (error) {
92+
throw new Error('spring-javaformat service is not ready, please hold for few seconds')
93+
}
94+
}
95+
96+
async sendHeartbeat() {
97+
return axios.get(`http://localhost:${this.port}/health`)
98+
}
99+
100+
static getInstance(): FormatService {
101+
return this.instance
102+
}
103+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"compilerOptions": {
3+
"module": "commonjs",
4+
"target": "es6",
5+
"outDir": "out",
6+
"esModuleInterop": true,
7+
"lib": ["es6"],
8+
"sourceMap": true,
9+
"rootDir": "src",
10+
"strict": true /* enable all strict type-checking options */
11+
/* Additional Checks */
12+
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
13+
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
14+
// "noUnusedParameters": true, /* Report errors on unused parameters. */
15+
},
16+
"exclude": ["node_modules", ".vscode-test"]
17+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"rules": {
3+
"no-string-throw": true,
4+
"no-unused-expression": true,
5+
"no-duplicate-variable": true,
6+
"curly": true,
7+
"class-name": true,
8+
"semicolon": [
9+
true,
10+
"never"
11+
],
12+
"triple-equals": true
13+
},
14+
"defaultSeverity": "warning"
15+
}

‎spring-javaformat-vscode/spring-javaformat/yarn.lock

Lines changed: 388 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.