diff --git a/.vscode/settings.json b/.vscode/settings.json index c3499e26..76c749c3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,22 +1,26 @@ { - "javascript.format.placeOpenBraceOnNewLineForFunctions": true, - "javascript.format.placeOpenBraceOnNewLineForControlBlocks": true, - "typescript.format.placeOpenBraceOnNewLineForFunctions": true, - "typescript.format.placeOpenBraceOnNewLineForControlBlocks": true, - "cSpell.words": [ - "asciidoctor", - "deserialize", - "helvetica", - "joaompinto", - "junit", - "linkify", - "neue", - "plantuml", - "segoe", - "webview" - ], - "files.watcherExclude": { - "out/**": true, - "dist/**": true - }, + "javascript.format.placeOpenBraceOnNewLineForFunctions": true, + "javascript.format.placeOpenBraceOnNewLineForControlBlocks": true, + "typescript.format.placeOpenBraceOnNewLineForFunctions": true, + "typescript.format.placeOpenBraceOnNewLineForControlBlocks": true, + "cSpell.words": [ + "asciidoctor", + "deserialize", + "helvetica", + "joaompinto", + "junit", + "linkify", + "neue", + "plantuml", + "segoe", + "webview" + ], + "files.watcherExclude": { + "out/**": true, + "dist/**": true + }, + "editor.formatOnSave": false, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + } } diff --git a/package.json b/package.json index 7ab70e89..83094463 100644 --- a/package.json +++ b/package.json @@ -582,6 +582,23 @@ "markdownDescription": "%asciidoc.antora.showEnableAntoraPrompt.desc%" } } + }, + { + "order": 27, + "id": "findFiles", + "title": "%asciidoc.findFiles.title%", + "properties": { + "asciidoc.findFiles.useRipgrep": { + "type": "boolean", + "default": false, + "markdownDescription": "%asciidoc.findFiles.useRipgrep.desc%" + }, + "asciidoc.findFiles.ripgrepPath": { + "type": "string", + "default": "", + "markdownDescription": "%asciidoc.findFiles.ripgrepPath.desc%" + } + } } ], "configurationDefaults": { diff --git a/package.nls.de.json b/package.nls.de.json index 11ccaac9..27771492 100644 --- a/package.nls.de.json +++ b/package.nls.de.json @@ -73,5 +73,8 @@ "asciidoc.antora.enableAntoraSupport.deprecationMessage": "This setting has been replaced by `#asciidoc.antora.showEnableAntoraPrompt#` and no longer has any effect.", "asciidoc.antora.title": "Antora", "asciidoc.antora.enableAntoraSupport.desc": "Aktiviere [Antora](https://antora.org/) Unterstützung.", - "asciidoc.antora.showEnableAntoraPrompt.desc": "Show a prompt to enable [Antora](https://antora.org/) support when an antora.yml file is detected." + "asciidoc.antora.showEnableAntoraPrompt.desc": "Show a prompt to enable [Antora](https://antora.org/) support when an antora.yml file is detected.", + "asciidoc.findFiles.title": "Find files", + "asciidoc.findFiles.useRipgrep.desc": "Use ripgrep to search for files in the workspace.", + "asciidoc.findFiles.ripgrepPath.desc": "External `rg` command to execute. It accepts a full path to the binary, for instance: `/path/to/rg`. If the value is empty, use ripgrep from VSCode's built-in version." } \ No newline at end of file diff --git a/package.nls.fr.json b/package.nls.fr.json index dd23dcc1..cec3b9b4 100644 --- a/package.nls.fr.json +++ b/package.nls.fr.json @@ -73,5 +73,8 @@ "asciidoc.antora.enableAntoraSupport.deprecationMessage": "Ce paramètre a été remplacé par `#asciidoc.antora.showEnableAntoraPrompt#` et n'a plus aucun effet.", "asciidoc.antora.title": "Antora", "asciidoc.antora.enableAntoraSupport.desc": "Active le support [Antora](https://antora.org/).", - "asciidoc.antora.showEnableAntoraPrompt.desc": "Affiche une fenêtre contextuelle permettant d'activer le support [Antora](https://antora.org/) quand un fichier antora.yml est trouvé." + "asciidoc.antora.showEnableAntoraPrompt.desc": "Affiche une fenêtre contextuelle permettant d'activer le support [Antora](https://antora.org/) quand un fichier antora.yml est trouvé.", + "asciidoc.findFiles.title": "Find files", + "asciidoc.findFiles.useRipgrep.desc": "Utiliser ripgrep pour rechercher des fichiers dans l'espace de travail.", + "asciidoc.findFiles.ripgrepPath.desc": "Commande externe `rg` à exécuter. Accepte un chemin complet vers le binaire, par exemple : `/path/to/rg`. Si la valeur est vide, utilise ripgrep de la version intégrée de VSCode." } \ No newline at end of file diff --git a/package.nls.ja.json b/package.nls.ja.json index f167846d..354300d5 100644 --- a/package.nls.ja.json +++ b/package.nls.ja.json @@ -73,5 +73,8 @@ "asciidoc.antora.enableAntoraSupport.deprecationMessage": "This setting has been replaced by `#asciidoc.antora.showEnableAntoraPrompt#` and no longer has any effect.", "asciidoc.antora.title": "Antora", "asciidoc.antora.enableAntoraSupport.desc": "[Antora](https://antora.org/)サポートを有効にします。", - "asciidoc.antora.showEnableAntoraPrompt.desc": "Show a prompt to enable [Antora](https://antora.org/) support when an antora.yml file is detected." + "asciidoc.antora.showEnableAntoraPrompt.desc": "Show a prompt to enable [Antora](https://antora.org/) support when an antora.yml file is detected.", + "asciidoc.findFiles.title": "Find files", + "asciidoc.findFiles.useRipgrep.desc": "Use ripgrep to search for files in the workspace.", + "asciidoc.findFiles.ripgrepPath.desc": "External `rg` command to execute. It accepts a full path to the binary, for instance: `/path/to/rg`. If the value is empty, use ripgrep from VSCode's built-in version." } \ No newline at end of file diff --git a/package.nls.json b/package.nls.json index 8a5573d9..36c072c5 100644 --- a/package.nls.json +++ b/package.nls.json @@ -82,5 +82,9 @@ "asciidoc.antora.title": "Antora", "asciidoc.antora.enableAntoraSupport.desc": "Enable [Antora](https://antora.org/) support.", - "asciidoc.antora.showEnableAntoraPrompt.desc": "Show a prompt to enable [Antora](https://antora.org/) support when an antora.yml file is detected." + "asciidoc.antora.showEnableAntoraPrompt.desc": "Show a prompt to enable [Antora](https://antora.org/) support when an antora.yml file is detected.", + + "asciidoc.findFiles.title": "Find files", + "asciidoc.findFiles.useRipgrep.desc": "Use ripgrep to search for files in the workspace.", + "asciidoc.findFiles.ripgrepPath.desc": "External `rg` command to execute. It accepts a full path to the binary, for instance: `/path/to/rg`. If the value is empty, use ripgrep from VSCode's built-in version." } diff --git a/src/util/findFiles.ts b/src/util/findFiles.ts index e8de4fe7..55ba166a 100644 --- a/src/util/findFiles.ts +++ b/src/util/findFiles.ts @@ -1,9 +1,69 @@ +import { spawn } from 'child_process' +import ospath from 'path' import vscode, { Uri } from 'vscode' +import { getWorkspaceFolders } from './workspace' /** * Find files across all workspace folders in the workspace using a glob expression. + * Use `@vscode/ripgrep` to find files when there is a platform shell present. * @param glob A glob pattern that defines the files to search for. */ export async function findFiles (glob: string): Promise { - return vscode.workspace.findFiles(glob) + const isBrowser = ((process as any)?.browser === true) + const useRipgrep = (vscode.workspace.getConfiguration('asciidoc', null).get('findFiles.useRipgrep') === true) + + if (isBrowser || !useRipgrep) { + return vscode.workspace.findFiles(glob) + } + const searchedUris: Uri[] = [] + for (const workspaceFolder of getWorkspaceFolders()) { + const rootUri = workspaceFolder.uri + const paths = await ripgrep(glob, rootUri.fsPath) + searchedUris.push(...paths.map((path) => Uri.joinPath(rootUri, path))) + } + return searchedUris +} + +async function ripgrep (glob: string, rootFolder: string): Promise { + const config = vscode.workspace.getConfiguration('asciidoc.findFiles') + const customPath = config.get('ripgrepPath') + const rgPath = customPath || ospath.join( + vscode.env.appRoot, + `node_modules/@vscode/ripgrep/bin/rg${ + process.platform === 'win32' ? '.exe' : '' + }` + ) + return new Promise((resolve, reject) => { + const rg = spawn(rgPath, ['--hidden', '--follow', '--files', '-g', glob], { + cwd: rootFolder, + }) + let stdout: string = '' + let stderr = '' + + rg.stdout.on('data', (data) => { + stdout += data.toString() + }) + + rg.stderr.on('data', (data) => { + stderr += data.toString() + }) + + rg.on('close', (code) => { + if (code === 0) { + const result = stdout + .split('\n') + .map((path) => path.trim()) + .filter((path) => !!path) // ensure empty strings are deleted from answer + resolve(result) + } else if (code === 1) { + resolve([]) + } else { + reject(new Error(`code ${code}: ${stderr}`)) + } + }) + + rg.on('error', (err) => { + reject(err) + }) + }) }