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 Oct 1, 2024
1 parent cf6db79 commit abf0a1c
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 118 deletions.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
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
3 changes: 2 additions & 1 deletion src/language-service/src/haConfig/haConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ export class HomeAssistantConfiguration {
};

private getRootFiles = (): string[] => {
const filesInRoot = this.fileAccessor.getFilesInFolder("");
console.log(`Searching from "${path.join(this.fileAccessor.getRoot(), 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,21 +47,22 @@ 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));
});
} catch (err) {
console.log(`Cannot find the files in folder ${subFolder}`);
console.log(`Cannot find the files in folder ${subFolder}: ${err}`);
}
return filelist;
}
Expand Down
210 changes: 113 additions & 97 deletions src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,116 +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 @@ -179,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 abf0a1c

Please sign in to comment.