Skip to content

Commit

Permalink
feat(perf): allow configuring search path
Browse files Browse the repository at this point in the history
  • Loading branch information
pdecat committed Dec 3, 2023
1 parent 38491a2 commit 143c343
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 120 deletions.
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "vscode-home-assistant",
"displayName": "Home Assistant Config Helper",
"description": " Completion for entity-id's in Home Assistant Configurations",
"version": "1.39.0",
"version": "1.39.1-dev",
"preview": false,
"engines": {
"vscode": "^1.45.1"
Expand Down Expand Up @@ -55,6 +55,11 @@
"vscode-home-assistant.ignoreCertificates": {
"type": "boolean",
"description": "Enable insecure transport. Check this if you want to connect over an insecure HTTPS transport with a invalid certificate!"
},
"vscode-home-assistant.searchPath": {
"type": "string",
"description": "The path from which your Home Assistant configuration files will be searched. Empty by default, so all files in your workspace will be evaluated. On large workspaces, setting this specifically to the path where your configuration files are located can improve performance drastically. Path can be absolute or relative to the workspace root.",
"default": ""
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions src/language-service/src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as vscodeUri from "vscode-uri";

export interface IConfigurationService {
isConfigured: boolean;
searchPath?: string;
token?: string;
url?: string;
ignoreCertificates: boolean;
Expand All @@ -13,11 +14,14 @@ export interface HomeAssistantConfiguration {
longLivedAccessToken?: string;
hostUrl?: string;
ignoreCertificates: boolean;
searchPath?: string;
}

export class ConfigurationService implements IConfigurationService {
public isConfigured = false;

public searchPath?: string;

public token?: string;

public url?: string;
Expand All @@ -41,13 +45,17 @@ export class ConfigurationService implements IConfigurationService {
this.url = this.getUri(incoming.hostUrl);
}
this.ignoreCertificates = !!incoming.ignoreCertificates;
this.searchPath = incoming.searchPath;

this.setConfigViaEnvironmentVariables();

this.isConfigured = `${this.url}` !== "";
};

private setConfigViaEnvironmentVariables() {
if (!this.searchPath && process.env.HASS_SEARCH_PATH) {
this.searchPath = this.getUri(process.env.HASS_SEARCH_PATH);
}
if (!this.url && process.env.HASS_SERVER) {
this.url = this.getUri(process.env.HASS_SERVER);
}
Expand Down
1 change: 1 addition & 0 deletions src/language-service/src/fileAccessor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface FileAccessor {
getRoot(): string;
getFileContents(fileName: string): Promise<string>;
getFilesInFolder(subFolder: string): string[];
getFilesInFolderRelativeFrom(
Expand Down
4 changes: 3 additions & 1 deletion src/language-service/src/haConfig/haConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as path from "path";
import { FileAccessor } from "../fileAccessor";
import { HomeAssistantYamlFile } from "./haYamlFile";
import { ScriptReferences, HaFileInfo, IncludeReferences } from "./dto";
import { IConfigurationService } from "../configuration";

export class HomeAssistantConfiguration {
private files: FilesCollection;
Expand Down Expand Up @@ -99,7 +100,8 @@ export class HomeAssistantConfiguration {
};

private getRootFiles = (): string[] => {
const filesInRoot = this.fileAccessor.getFilesInFolder("");
console.log(`Searching from "${this.subFolder}"`);
const filesInRoot = this.fileAccessor.getFilesInFolder(this.subFolder);
const ourFiles = [
"configuration.yaml",
"ui-lovelace.yaml",
Expand Down
30 changes: 10 additions & 20 deletions src/server/fileAccessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,7 @@ import { TextDocument } from "vscode-languageserver-textdocument";
import * as fs from "fs";
import * as path from "path";
import * as vscodeUri from "vscode-uri";

export interface FileAccessor {
getFileContents(fileName: string): Promise<string>;
getFilesInFolder(subFolder: string): string[];
getFilesInFolderRelativeFrom(
subFolder: string,
relativeFrom: string,
): string[];
getFilesInFolderRelativeFromAsFileUri(
subFolder: string,
relativeFrom: string,
): string[];
getRelativePath(relativeFrom: string, filename: string): string;
getRelativePathAsFileUri(relativeFrom: string, filename: string): string;
fromUriToLocalPath(uri: string): string;
}
import { FileAccessor } from "../language-service/src/fileAccessor";

export class VsCodeFileAccessor implements FileAccessor {
private ourRoot: string;
Expand All @@ -27,7 +12,11 @@ export class VsCodeFileAccessor implements FileAccessor {
private workspaceFolder: string,
private documents: TextDocuments<TextDocument>,
) {
this.ourRoot = path.resolve();
this.ourRoot = path.resolve(workspaceFolder);
}

getRoot(): string {
return this.ourRoot;
}

public async getFileContents(uri: string): Promise<string> {
Expand Down Expand Up @@ -58,18 +47,19 @@ export class VsCodeFileAccessor implements FileAccessor {
filelist: string[] = [],
): string[] {
subFolder = path.normalize(subFolder);
console.log(`fileAccessor.tf:getFilesInFolder:subFolder: ${subFolder}`);

try {
fs.readdirSync(subFolder).forEach((file) => {
fs.readdirSync(path.join(this.ourRoot, subFolder)).forEach((file) => {
// ignore dot files
if (file.charAt(0) === ".") {
return;
}
filelist =
fs.statSync(path.join(subFolder, file)).isDirectory() &&
fs.statSync(path.join(this.ourRoot, subFolder, file)).isDirectory() &&
!file.startsWith(".")
? this.getFilesInFolder(path.join(subFolder, file), filelist)
: filelist.concat(path.join(subFolder, file));
: filelist.concat(path.join(this.ourRoot, subFolder, file));
});
} catch (err) {
console.log(`Cannot find the files in folder ${subFolder}`);
Expand Down
211 changes: 113 additions & 98 deletions src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,117 +32,135 @@ connection.onInitialize((params) => {
connection.console.log(
`[Home Assistant Language Server(${process.pid})] Started and initialize received`,
);

const configurationService = new ConfigurationService();

const haConnection = new HaConnection(configurationService);
const fileAccessor = new VsCodeFileAccessor(params.rootUri, documents);
const haConfig = new HomeAssistantConfiguration(fileAccessor);

const definitionProviders = [
new IncludeDefinitionProvider(fileAccessor),
new ScriptDefinitionProvider(haConfig),
];

const jsonWorkerContributions = [
new EntityIdCompletionContribution(haConnection),
new ServicesCompletionContribution(haConnection),
];

const schemaServiceForIncludes = new SchemaServiceForIncludes();

const yamlLanguageService = getLanguageService(
// eslint-disable-next-line @typescript-eslint/require-await
async () => "",
null,
jsonWorkerContributions,
);

const sendDiagnostics = (uri: string, diagnostics: Diagnostic[]) => {
connection.sendDiagnostics({
uri,
diagnostics,
});
};
// Wait for configuration to be loaded before initialising the rest
connection.onDidChangeConfiguration(async (config) => {
connection.console.log(
`[Home Assistant Language Server(${process.pid})] didChangeConfiguration received`,
)
configurationService.updateConfiguration(config);

const discoverFilesAndUpdateSchemas = async () => {
try {
await haConfig.discoverFiles();
homeAsisstantLanguageService.findAndApplySchemas();
} catch (e) {
console.error(
`Unexpected error during file discovery / schema configuration: ${e}`,
);
const haConnection = new HaConnection(configurationService);
console.log(`configurationService.url: ${configurationService.url}`)
console.log(`configurationService.searchPath: ${configurationService.searchPath}`)
console.log(`params.rootUri: ${params.rootUri}`)
let rootUri = params.rootUri;
if (configurationService.searchPath !== undefined) {
rootUri = configurationService.searchPath;
}
};
console.log(`rootUri: ${rootUri}`)
const fileAccessor = new VsCodeFileAccessor(rootUri, documents);
const haConfig = new HomeAssistantConfiguration(fileAccessor);

const definitionProviders = [
new IncludeDefinitionProvider(fileAccessor),
new ScriptDefinitionProvider(haConfig),
];

const jsonWorkerContributions = [
new EntityIdCompletionContribution(haConnection),
new ServicesCompletionContribution(haConnection),
];

const schemaServiceForIncludes = new SchemaServiceForIncludes();

const yamlLanguageService = getLanguageService(
// eslint-disable-next-line @typescript-eslint/require-await
async () => "",
null,
jsonWorkerContributions,
);

const homeAsisstantLanguageService = new HomeAssistantLanguageService(
yamlLanguageService,
haConfig,
haConnection,
definitionProviders,
schemaServiceForIncludes,
sendDiagnostics,
() => {
documents.all().forEach(async (d) => {
const diagnostics = await homeAsisstantLanguageService.getDiagnostics(
d,
);
sendDiagnostics(d.uri, diagnostics);
const sendDiagnostics = (uri: string, diagnostics: Diagnostic[]) => {
connection.sendDiagnostics({
uri,
diagnostics,
});
},
);
};

const discoverFilesAndUpdateSchemas = async () => {
try {
await haConfig.discoverFiles();
homeAsisstantLanguageService.findAndApplySchemas();
} catch (e) {
console.error(
`Unexpected error during file discovery / schema configuration: ${e}`,
);
}
};

const homeAsisstantLanguageService = new HomeAssistantLanguageService(
yamlLanguageService,
haConfig,
haConnection,
definitionProviders,
schemaServiceForIncludes,
sendDiagnostics,
() => {
documents.all().forEach(async (d) => {
const diagnostics = await homeAsisstantLanguageService.getDiagnostics(
d,
);
sendDiagnostics(d.uri, diagnostics);
});
},
);

documents.onDidChangeContent((e) =>
homeAsisstantLanguageService.onDocumentChange(e),
);
documents.onDidOpen((e) => homeAsisstantLanguageService.onDocumentOpen(e));
documents.onDidChangeContent((e) =>
homeAsisstantLanguageService.onDocumentChange(e),
);
documents.onDidOpen((e) => homeAsisstantLanguageService.onDocumentOpen(e));

let onDidSaveDebounce: NodeJS.Timer;
documents.onDidSave(() => {
clearTimeout(onDidSaveDebounce);
onDidSaveDebounce = setTimeout(discoverFilesAndUpdateSchemas, 100);
});
let onDidSaveDebounce: NodeJS.Timer;
documents.onDidSave(() => {
clearTimeout(onDidSaveDebounce);
onDidSaveDebounce = setTimeout(discoverFilesAndUpdateSchemas, 100);
});

connection.onDocumentSymbol((p) =>
homeAsisstantLanguageService.onDocumentSymbol(
documents.get(p.textDocument.uri),
),
);
connection.onDocumentFormatting((p) =>
homeAsisstantLanguageService.onDocumentFormatting(
documents.get(p.textDocument.uri),
p.options,
),
);
connection.onCompletion((p) =>
homeAsisstantLanguageService.onCompletion(
documents.get(p.textDocument.uri),
p.position,
),
);
connection.onCompletionResolve((p) =>
homeAsisstantLanguageService.onCompletionResolve(p),
);
connection.onHover((p) =>
homeAsisstantLanguageService.onHover(
documents.get(p.textDocument.uri),
p.position,
),
);
connection.onDefinition((p) =>
homeAsisstantLanguageService.onDefinition(
documents.get(p.textDocument.uri),
p.position,
),
);
connection.onDocumentSymbol((p) =>
homeAsisstantLanguageService.onDocumentSymbol(
documents.get(p.textDocument.uri),
),
);
connection.onDocumentFormatting((p) =>
homeAsisstantLanguageService.onDocumentFormatting(
documents.get(p.textDocument.uri),
p.options,
),
);
connection.onCompletion((p) =>
homeAsisstantLanguageService.onCompletion(
documents.get(p.textDocument.uri),
p.position,
),
);
connection.onCompletionResolve((p) =>
homeAsisstantLanguageService.onCompletionResolve(p),
);
connection.onHover((p) =>
homeAsisstantLanguageService.onHover(
documents.get(p.textDocument.uri),
p.position,
),
);
connection.onDefinition((p) =>
homeAsisstantLanguageService.onDefinition(
documents.get(p.textDocument.uri),
p.position,
),
);

connection.onDidChangeConfiguration(async (config) => {
configurationService.updateConfiguration(config);
await haConnection.notifyConfigUpdate();

if (!configurationService.isConfigured) {
connection.sendNotification("no-config");
}

// fire and forget
setTimeout(discoverFilesAndUpdateSchemas, 0);
});

connection.onRequest(
Expand Down Expand Up @@ -180,9 +198,6 @@ connection.onInitialize((params) => {
connection.sendNotification("render_template_completed", outputString);
});

// fire and forget
setTimeout(discoverFilesAndUpdateSchemas, 0);

return {
capabilities: <ServerCapabilities>{
textDocumentSync: TextDocumentSyncKind.Full,
Expand Down

0 comments on commit 143c343

Please sign in to comment.