diff --git a/README.md b/README.md index 95d1e13..8be46a8 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ It has been tested with: |:----------------------------------|:---------------:|:------------------:| | Syntax Highlighting | ✅ | ✅ | | Code Snippets | ✅ | ✅ | -| Live Template Analysis | ✅ | ✅ | +| Live Template Analysis | ✅ | ✅ | | HTML Language Features | ✅ | - | | ViewHelper Autocomplete (tags) | *only built-in* | - | | ViewHelper Documentation (tags) | *only built-in* | - | @@ -35,9 +35,13 @@ It has been tested with: ### Live Template Analysis The extension is able to utilize available binaries in your project to provide live -template analysis for Fluid templates, e. g. for detecting syntax errors or deprecations. -This works out-of-the-box for Fluid 5.3 (or higher) and TYPO3 14.3 (or higher). The -following folders are checked for the `fluid` and `typo3` binaries: +template analysis for Fluid templates, e. g. to detect syntax errors or deprecations. +This works out-of-the-box for Fluid 5.3 (or higher) and TYPO3 14.3 (or higher). In older +TYPO3 versions the +[companion extension](https://extensions.typo3.org/extension/fluid_companion) needs to +be installed. + +The following folders are checked for the `fluid` and `typo3` binaries: * `vendor/bin/` * `bin/` @@ -54,9 +58,6 @@ Custom paths to the binaries can also be specified in the extension's configurat path of the current workplace folder. Apart from that, these are currently **not** preprocessed, so be extra careful. -Compatibility with older versions of TYPO3 might still be feasible, but this is not -implemented yet. - ### ViewHelper Autocomplete & Documentation The extension currently doesn't include a dedicated diff --git a/client/src/extension.ts b/client/src/extension.ts index e64892a..d2d2ce1 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -77,6 +77,50 @@ const updateDiagnostics = debounce((document: TextDocument, collection: Diagnost if (!analyzeResult) { collection.delete(document.uri); if (analyzeResult === false) { + const typo3Version = detectTypo3Version(document); + if (typo3Version === 12 || typo3Version === 13) { + vscode.window.showInformationMessage( + `To be able to provide live analysis for Fluid templates in TYPO3 ${typo3Version}, a companion TYPO3 extension needs to be installed.`, + { identifier: 'more', title: 'Learn more' }, + { identifier: 'disable', title: 'Disable for workspace' }, + ).then(userOption => { + switch (userOption?.identifier) { + case 'more': + vscode.env.openExternal( + vscode.Uri.parse('https://extensions.typo3.org/extension/fluid_companion'), + ); + break; + + case 'disable': + vscode.workspace.getConfiguration('fluid.features').update( + 'liveTemplateAnalysis', + false, + vscode.ConfigurationTarget.Workspace + ); + break; + } + }); + return; + } + + if (typo3Version && typo3Version < 12) { + vscode.window.showInformationMessage( + `Live analysis for Fluid templates is not compatible with TYPO3 ${typo3Version}.`, + { identifier: 'disable', title: 'Disable for workspace' }, + ).then(userOption => { + switch (userOption?.identifier) { + case 'disable': + vscode.workspace.getConfiguration('fluid.features').update( + 'liveTemplateAnalysis', + false, + vscode.ConfigurationTarget.Workspace + ); + break; + } + }); + return; + } + logChannel.info('Try increasing the log level to "debug" to get more information about the issue.'); vscode.window.showInformationMessage( 'Unable to provide live analysis for Fluid templates in this workspace.', @@ -211,6 +255,52 @@ function analyzeTemplate(document: TextDocument): TemplateValidatorResult|null|f return false; } +function detectTypo3Version(document: TextDocument): number|null +{ + const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri)?.uri.fsPath; + if (!workspaceFolder) { + return null; + } + + // Go through binary alternatives to find the best candidate + const candidates: BinaryCommand[] = []; + const isDdevProject = config.bin.useDdevIfAvailable ? fs.existsSync(path.join(workspaceFolder, '.ddev')) : false; + const ddev = isDdevProject ? spawnSync('which', ['ddev'], { shell: true }).stdout.toString().trim() : ''; + + // TODO optimize this to avoid duplicate calls to non-existent binaries + if (config.bin.typo3) { + candidates.push({ + command: config.bin.typo3.replaceAll('${workspaceFolder}', workspaceFolder), + args: ['--version'], + userDefined: true, + }); + } + if (isDdevProject && ddev) { + candidates.push({ + command: ddev, + args: ['typo3', '--version'], + }); + } + for (const typo3Binary of ['vendor/bin/typo3', 'bin/typo3', '.Build/bin/typo3']) { + candidates.push({ + command: path.join(workspaceFolder, typo3Binary), + args: ['--version'], + }); + } + for (const candidate of candidates) { + const process = spawnSync(candidate.command, candidate.args); + if (!process.stdout) { + continue; + } + const versionOutput = process.stdout?.toString() ?? ''; + const versionMatch = versionOutput.match(/TYPO3 CMS ([0-9]+)\.[0-9]+\.[0-9]+/); + if (versionMatch && versionMatch[1]) { + return Number(versionMatch[1]); + } + } + return null; +} + function tryAndVerifyAnalyzeCommand(command: BinaryCommand, input: string, cwd: string): TemplateValidatorResult|null { const readableCommand = [command.command, ...command.args].join(' '); let rawResult, rawError, errorCode;