@herb-tools/language-server 0.8.3 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -34
- package/dist/diagnostics.js +51 -0
- package/dist/diagnostics.js.map +1 -1
- package/dist/document_save_service.js +18 -1
- package/dist/document_save_service.js.map +1 -1
- package/dist/formatting_service.js +3 -0
- package/dist/formatting_service.js.map +1 -1
- package/dist/herb-language-server.js +4972 -4830
- package/dist/herb-language-server.js.map +1 -1
- package/dist/index.cjs +76 -6
- package/dist/index.cjs.map +1 -1
- package/dist/server.js +2 -3
- package/dist/server.js.map +1 -1
- package/dist/settings.js +2 -1
- package/dist/settings.js.map +1 -1
- package/dist/types/diagnostics.d.ts +11 -1
- package/dist/types/document_save_service.d.ts +9 -0
- package/package.json +5 -5
- package/src/diagnostics.ts +64 -1
- package/src/document_save_service.ts +20 -1
- package/src/formatting_service.ts +3 -0
- package/src/server.ts +2 -6
- package/src/settings.ts +2 -1
- package/dist/config.js +0 -73
- package/dist/config.js.map +0 -1
- package/dist/types/config.d.ts +0 -34
- package/src/config.ts +0 -109
package/src/diagnostics.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { TextDocument } from "vscode-languageserver-textdocument"
|
|
2
|
-
import { Connection, Diagnostic } from "vscode-languageserver/node"
|
|
2
|
+
import { Connection, Diagnostic, DiagnosticSeverity, DiagnosticTag } from "vscode-languageserver/node"
|
|
3
|
+
import { Visitor } from "@herb-tools/core"
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
Node,
|
|
7
|
+
ERBCaseNode,
|
|
8
|
+
ERBCaseMatchNode,
|
|
9
|
+
HTMLTextNode,
|
|
10
|
+
} from "@herb-tools/core"
|
|
11
|
+
|
|
12
|
+
import { isHTMLTextNode } from "@herb-tools/core"
|
|
3
13
|
|
|
4
14
|
import { ParserService } from "./parser_service"
|
|
5
15
|
import { LinterService } from "./linter_service"
|
|
@@ -36,10 +46,12 @@ export class Diagnostics {
|
|
|
36
46
|
} else {
|
|
37
47
|
const parseResult = this.parserService.parseDocument(textDocument)
|
|
38
48
|
const lintResult = await this.linterService.lintDocument(textDocument)
|
|
49
|
+
const unreachableCodeDiagnostics = this.getUnreachableCodeDiagnostics(parseResult.document)
|
|
39
50
|
|
|
40
51
|
allDiagnostics = [
|
|
41
52
|
...parseResult.diagnostics,
|
|
42
53
|
...lintResult.diagnostics,
|
|
54
|
+
...unreachableCodeDiagnostics,
|
|
43
55
|
]
|
|
44
56
|
}
|
|
45
57
|
|
|
@@ -47,6 +59,12 @@ export class Diagnostics {
|
|
|
47
59
|
this.sendDiagnosticsFor(textDocument)
|
|
48
60
|
}
|
|
49
61
|
|
|
62
|
+
private getUnreachableCodeDiagnostics(document: Node): Diagnostic[] {
|
|
63
|
+
const collector = new UnreachableCodeCollector()
|
|
64
|
+
collector.visit(document)
|
|
65
|
+
return collector.diagnostics
|
|
66
|
+
}
|
|
67
|
+
|
|
50
68
|
async refreshDocument(document: TextDocument) {
|
|
51
69
|
await this.validate(document)
|
|
52
70
|
}
|
|
@@ -67,3 +85,48 @@ export class Diagnostics {
|
|
|
67
85
|
this.diagnostics.delete(textDocument)
|
|
68
86
|
}
|
|
69
87
|
}
|
|
88
|
+
|
|
89
|
+
export class UnreachableCodeCollector extends Visitor {
|
|
90
|
+
diagnostics: Diagnostic[] = []
|
|
91
|
+
|
|
92
|
+
visitERBCaseNode(node: ERBCaseNode): void {
|
|
93
|
+
this.checkUnreachableChildren(node.children)
|
|
94
|
+
this.visitChildNodes(node)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
visitERBCaseMatchNode(node: ERBCaseMatchNode): void {
|
|
98
|
+
this.checkUnreachableChildren(node.children)
|
|
99
|
+
this.visitChildNodes(node)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private checkUnreachableChildren(children: Node[]): void {
|
|
103
|
+
for (const child of children) {
|
|
104
|
+
if (isHTMLTextNode(child) && child.content.trim() === "") {
|
|
105
|
+
continue
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const diagnostic: Diagnostic = {
|
|
109
|
+
range: {
|
|
110
|
+
start: {
|
|
111
|
+
line: this.toZeroBased(child.location.start.line),
|
|
112
|
+
character: child.location.start.column
|
|
113
|
+
},
|
|
114
|
+
end: {
|
|
115
|
+
line: this.toZeroBased(child.location.end.line),
|
|
116
|
+
character: child.location.end.column
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
message: "Unreachable code: content between case and when/in is never executed",
|
|
120
|
+
severity: DiagnosticSeverity.Hint,
|
|
121
|
+
tags: [DiagnosticTag.Unnecessary],
|
|
122
|
+
source: "Herb Language Server"
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
this.diagnostics.push(diagnostic)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private toZeroBased(line: number): number {
|
|
130
|
+
return line - 1
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -18,12 +18,31 @@ export class DocumentSaveService {
|
|
|
18
18
|
this.formattingService = formattingService
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Apply only autofix edits on save.
|
|
23
|
+
* Called by willSaveWaitUntil - formatting is handled separately by editor.formatOnSave
|
|
24
|
+
*/
|
|
25
|
+
async applyFixes(document: TextDocument): Promise<TextEdit[]> {
|
|
26
|
+
const settings = await this.settings.getDocumentSettings(document.uri)
|
|
27
|
+
const fixOnSave = settings?.linter?.fixOnSave !== false
|
|
28
|
+
|
|
29
|
+
this.connection.console.log(`[DocumentSave] applyFixes fixOnSave=${fixOnSave}`)
|
|
30
|
+
|
|
31
|
+
if (!fixOnSave) return []
|
|
32
|
+
|
|
33
|
+
return this.autofixService.autofix(document)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Apply autofix and formatting.
|
|
38
|
+
* Called by onDocumentFormatting (manual format or editor.formatOnSave)
|
|
39
|
+
*/
|
|
21
40
|
async applyFixesAndFormatting(document: TextDocument, reason: TextDocumentSaveReason): Promise<TextEdit[]> {
|
|
22
41
|
const settings = await this.settings.getDocumentSettings(document.uri)
|
|
23
42
|
const fixOnSave = settings?.linter?.fixOnSave !== false
|
|
24
43
|
const formatterEnabled = settings?.formatter?.enabled ?? false
|
|
25
44
|
|
|
26
|
-
this.connection.console.log(`[DocumentSave] fixOnSave=${fixOnSave}, formatterEnabled=${formatterEnabled}`)
|
|
45
|
+
this.connection.console.log(`[DocumentSave] applyFixesAndFormatting fixOnSave=${fixOnSave}, formatterEnabled=${formatterEnabled}`)
|
|
27
46
|
|
|
28
47
|
let autofixEdits: TextEdit[] = []
|
|
29
48
|
|
|
@@ -210,6 +210,9 @@ export class FormattingService {
|
|
|
210
210
|
if (filePath.endsWith('.herb.yml')) return false
|
|
211
211
|
if (!this.config) return true
|
|
212
212
|
|
|
213
|
+
const hasConfigFile = Config.exists(this.config.projectPath)
|
|
214
|
+
if (!hasConfigFile) return true
|
|
215
|
+
|
|
213
216
|
const relativePath = filePath.replace('file://', '').replace(this.project.projectPath + '/', '')
|
|
214
217
|
|
|
215
218
|
return this.config.isFormatterEnabledForPath(relativePath)
|
package/src/server.ts
CHANGED
|
@@ -12,15 +12,12 @@ import {
|
|
|
12
12
|
DocumentRangeFormattingParams,
|
|
13
13
|
CodeActionParams,
|
|
14
14
|
CodeActionKind,
|
|
15
|
-
TextEdit,
|
|
16
15
|
} from "vscode-languageserver/node"
|
|
17
16
|
|
|
18
17
|
import { Service } from "./service"
|
|
19
18
|
import { PersonalHerbSettings } from "./settings"
|
|
20
19
|
import { Config } from "@herb-tools/config"
|
|
21
20
|
|
|
22
|
-
import type { TextDocument } from "vscode-languageserver-textdocument"
|
|
23
|
-
|
|
24
21
|
export class Server {
|
|
25
22
|
private service!: Service
|
|
26
23
|
private connection: Connection
|
|
@@ -37,7 +34,7 @@ export class Server {
|
|
|
37
34
|
await this.service.init()
|
|
38
35
|
|
|
39
36
|
this.service.documentService.documents.onWillSaveWaitUntil(async (event) => {
|
|
40
|
-
return this.service.documentSaveService.
|
|
37
|
+
return this.service.documentSaveService.applyFixes(event.document)
|
|
41
38
|
})
|
|
42
39
|
|
|
43
40
|
const result: InitializeResult = {
|
|
@@ -89,7 +86,6 @@ export class Server {
|
|
|
89
86
|
watchers: [
|
|
90
87
|
...patterns,
|
|
91
88
|
{ globPattern: `**/.herb.yml` },
|
|
92
|
-
{ globPattern: `**/**/.herb-lsp/config.json` },
|
|
93
89
|
{ globPattern: `**/.herb/rules/**/*.mjs` },
|
|
94
90
|
{ globPattern: `**/.herb/rewriters/**/*.mjs` },
|
|
95
91
|
],
|
|
@@ -119,7 +115,7 @@ export class Server {
|
|
|
119
115
|
|
|
120
116
|
this.connection.onDidChangeWatchedFiles(async (params) => {
|
|
121
117
|
for (const event of params.changes) {
|
|
122
|
-
const isConfigChange = event.uri.endsWith("/.herb.yml")
|
|
118
|
+
const isConfigChange = event.uri.endsWith("/.herb.yml")
|
|
123
119
|
const isCustomRuleChange = event.uri.includes("/.herb/rules/")
|
|
124
120
|
const isCustomRewriterChange = event.uri.includes("/.herb/rewriters/")
|
|
125
121
|
|
package/src/settings.ts
CHANGED
|
@@ -87,8 +87,9 @@ export class Settings {
|
|
|
87
87
|
// TODO: ideally we can just use Config all the way through
|
|
88
88
|
private mergeSettings(userSettings: PersonalHerbSettings | null, projectConfig?: Config): PersonalHerbSettings {
|
|
89
89
|
const settings = userSettings || this.defaultSettings
|
|
90
|
+
const hasConfigFile = projectConfig ? Config.exists(projectConfig.projectPath) : false
|
|
90
91
|
|
|
91
|
-
if (!projectConfig) {
|
|
92
|
+
if (!projectConfig || !hasConfigFile) {
|
|
92
93
|
return {
|
|
93
94
|
trace: settings.trace,
|
|
94
95
|
linter: {
|
package/dist/config.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import { version } from "../package.json";
|
|
3
|
-
import { promises as fs } from "fs";
|
|
4
|
-
export class Config {
|
|
5
|
-
constructor(projectPath, config) {
|
|
6
|
-
this.path = Config.configPathFromProjectPath(projectPath);
|
|
7
|
-
this.config = config;
|
|
8
|
-
}
|
|
9
|
-
get version() {
|
|
10
|
-
return this.config.version;
|
|
11
|
-
}
|
|
12
|
-
get createdAt() {
|
|
13
|
-
return new Date(this.config.createdAt);
|
|
14
|
-
}
|
|
15
|
-
get updatedAt() {
|
|
16
|
-
return new Date(this.config.updatedAt);
|
|
17
|
-
}
|
|
18
|
-
get options() {
|
|
19
|
-
return this.config.options;
|
|
20
|
-
}
|
|
21
|
-
toJSON() {
|
|
22
|
-
return JSON.stringify(this.config, null, " ");
|
|
23
|
-
}
|
|
24
|
-
updateTimestamp() {
|
|
25
|
-
this.config.updatedAt = new Date().toISOString();
|
|
26
|
-
}
|
|
27
|
-
updateVersion() {
|
|
28
|
-
this.config.version = version;
|
|
29
|
-
}
|
|
30
|
-
async write() {
|
|
31
|
-
this.updateVersion();
|
|
32
|
-
this.updateTimestamp();
|
|
33
|
-
const folder = path.dirname(this.path);
|
|
34
|
-
fs.stat(folder)
|
|
35
|
-
.then(() => { })
|
|
36
|
-
.catch(async () => await fs.mkdir(folder))
|
|
37
|
-
.finally(async () => await fs.writeFile(this.path, this.toJSON()));
|
|
38
|
-
}
|
|
39
|
-
async read() {
|
|
40
|
-
return await fs.readFile(this.path, "utf8");
|
|
41
|
-
}
|
|
42
|
-
static configPathFromProjectPath(projectPath) {
|
|
43
|
-
return path.join(projectPath, this.configPath);
|
|
44
|
-
}
|
|
45
|
-
static async fromPathOrNew(projectPath) {
|
|
46
|
-
try {
|
|
47
|
-
return await this.fromPath(projectPath);
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
return Config.newConfig(projectPath);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
static async fromPath(projectPath) {
|
|
54
|
-
const configPath = Config.configPathFromProjectPath(projectPath);
|
|
55
|
-
try {
|
|
56
|
-
const config = JSON.parse(await fs.readFile(configPath, "utf8"));
|
|
57
|
-
return new Config(projectPath, config);
|
|
58
|
-
}
|
|
59
|
-
catch (error) {
|
|
60
|
-
throw new Error(`Error reading config file at: ${configPath}. Error: ${error.message}`);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
static newConfig(projectPath) {
|
|
64
|
-
return new Config(projectPath, {
|
|
65
|
-
version,
|
|
66
|
-
createdAt: new Date().toISOString(),
|
|
67
|
-
updatedAt: new Date().toISOString(),
|
|
68
|
-
options: {}
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
Config.configPath = ".herb-lsp/config.json";
|
|
73
|
-
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/types/config.d.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
export type HerbConfigOptions = {
|
|
2
|
-
formatter?: {
|
|
3
|
-
enabled?: boolean;
|
|
4
|
-
include?: string[];
|
|
5
|
-
exclude?: string[];
|
|
6
|
-
indentWidth?: number;
|
|
7
|
-
maxLineLength?: number;
|
|
8
|
-
};
|
|
9
|
-
};
|
|
10
|
-
export type HerbLSPConfig = {
|
|
11
|
-
version: string;
|
|
12
|
-
createdAt: string;
|
|
13
|
-
updatedAt: string;
|
|
14
|
-
options: HerbConfigOptions;
|
|
15
|
-
};
|
|
16
|
-
export declare class Config {
|
|
17
|
-
static configPath: string;
|
|
18
|
-
readonly path: string;
|
|
19
|
-
config: HerbLSPConfig;
|
|
20
|
-
constructor(projectPath: string, config: HerbLSPConfig);
|
|
21
|
-
get version(): string;
|
|
22
|
-
get createdAt(): Date;
|
|
23
|
-
get updatedAt(): Date;
|
|
24
|
-
get options(): HerbConfigOptions;
|
|
25
|
-
toJSON(): string;
|
|
26
|
-
private updateTimestamp;
|
|
27
|
-
private updateVersion;
|
|
28
|
-
write(): Promise<void>;
|
|
29
|
-
read(): Promise<string>;
|
|
30
|
-
static configPathFromProjectPath(projectPath: string): string;
|
|
31
|
-
static fromPathOrNew(projectPath: string): Promise<Config>;
|
|
32
|
-
static fromPath(projectPath: string): Promise<Config>;
|
|
33
|
-
static newConfig(projectPath: string): Config;
|
|
34
|
-
}
|
package/src/config.ts
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
export type HerbConfigOptions = {
|
|
2
|
-
formatter?: {
|
|
3
|
-
enabled?: boolean
|
|
4
|
-
include?: string[]
|
|
5
|
-
exclude?: string[]
|
|
6
|
-
indentWidth?: number
|
|
7
|
-
maxLineLength?: number
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export type HerbLSPConfig = {
|
|
12
|
-
version: string
|
|
13
|
-
createdAt: string
|
|
14
|
-
updatedAt: string
|
|
15
|
-
options: HerbConfigOptions
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
import path from "path"
|
|
19
|
-
import { version } from "../package.json"
|
|
20
|
-
import { promises as fs } from "fs"
|
|
21
|
-
|
|
22
|
-
export class Config {
|
|
23
|
-
static configPath = ".herb-lsp/config.json"
|
|
24
|
-
|
|
25
|
-
public readonly path: string
|
|
26
|
-
public config: HerbLSPConfig
|
|
27
|
-
|
|
28
|
-
constructor(projectPath: string, config: HerbLSPConfig) {
|
|
29
|
-
this.path = Config.configPathFromProjectPath(projectPath)
|
|
30
|
-
this.config = config
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
get version(): string {
|
|
34
|
-
return this.config.version
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
get createdAt(): Date {
|
|
38
|
-
return new Date(this.config.createdAt)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
get updatedAt(): Date {
|
|
42
|
-
return new Date(this.config.updatedAt)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
get options(): HerbConfigOptions {
|
|
46
|
-
return this.config.options
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
public toJSON() {
|
|
50
|
-
return JSON.stringify(this.config, null, " ")
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
private updateTimestamp() {
|
|
54
|
-
this.config.updatedAt = new Date().toISOString()
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
private updateVersion() {
|
|
58
|
-
this.config.version = version
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async write() {
|
|
62
|
-
this.updateVersion()
|
|
63
|
-
this.updateTimestamp()
|
|
64
|
-
|
|
65
|
-
const folder = path.dirname(this.path)
|
|
66
|
-
|
|
67
|
-
fs.stat(folder)
|
|
68
|
-
.then(() => {})
|
|
69
|
-
.catch(async () => await fs.mkdir(folder))
|
|
70
|
-
.finally(async () => await fs.writeFile(this.path, this.toJSON()))
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async read() {
|
|
74
|
-
return await fs.readFile(this.path, "utf8")
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
static configPathFromProjectPath(projectPath: string) {
|
|
78
|
-
return path.join(projectPath, this.configPath)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
static async fromPathOrNew(projectPath: string) {
|
|
82
|
-
try {
|
|
83
|
-
return await this.fromPath(projectPath)
|
|
84
|
-
} catch {
|
|
85
|
-
return Config.newConfig(projectPath)
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
static async fromPath(projectPath: string) {
|
|
90
|
-
const configPath = Config.configPathFromProjectPath(projectPath)
|
|
91
|
-
|
|
92
|
-
try {
|
|
93
|
-
const config = JSON.parse(await fs.readFile(configPath, "utf8"))
|
|
94
|
-
|
|
95
|
-
return new Config(projectPath, config)
|
|
96
|
-
} catch (error: any) {
|
|
97
|
-
throw new Error(`Error reading config file at: ${configPath}. Error: ${error.message}`)
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
static newConfig(projectPath: string): Config {
|
|
102
|
-
return new Config(projectPath, {
|
|
103
|
-
version,
|
|
104
|
-
createdAt: new Date().toISOString(),
|
|
105
|
-
updatedAt: new Date().toISOString(),
|
|
106
|
-
options: {}
|
|
107
|
-
})
|
|
108
|
-
}
|
|
109
|
-
}
|