mirror of
https://github.com/mara-li/obsidian-my-thesaurus.git
synced 2026-07-22 12:00:26 +00:00
Compare commits
32 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be224777ff | ||
|
|
4199f7ca54 | ||
|
|
e2185fa767 | ||
|
|
99f274cbe2 | ||
|
|
714a8ed17d | ||
|
|
336bb21b17 | ||
|
|
04d40100e5 | ||
|
|
a9bc93800f | ||
|
|
25c6fab086 | ||
|
|
9ec3bb32c6 | ||
|
|
5abff9cf66 | ||
|
|
86fcbc23d0 | ||
|
|
df3f1c6dd6 | ||
|
|
1b50050700 | ||
|
|
4036c76e57 | ||
|
|
6d360588e6 | ||
|
|
cdbfe6750b | ||
|
|
61e0159615 | ||
|
|
dc2e2853e0 | ||
|
|
9af25f93eb | ||
|
|
59d663b877 | ||
|
|
25e7c38de2 | ||
|
|
ada642f7ee | ||
|
|
aa3b8ef598 | ||
|
|
087b88cb2c | ||
|
|
43a7702359 | ||
|
|
2cfd04a50e | ||
|
|
55a180c79d | ||
|
|
77b02f0462 | ||
|
|
1d6e018e30 | ||
|
|
465ca9e429 | ||
|
|
c253dafb0e |
20 changed files with 809 additions and 268 deletions
41
CHANGELOG.md
41
CHANGELOG.md
|
|
@ -1,3 +1,44 @@
|
|||
## [0.0.10](https://github.com/Mara-Li/obsidian-my-thesaurus/compare/0.0.9...0.0.10) (2025-02-27)
|
||||
|
||||
## [0.0.9](https://github.com/Mara-Li/obsidian-my-thesaurus/compare/0.0.8...0.0.9) (2025-02-24)
|
||||
### Features
|
||||
|
||||
* **settings:** add excludeTermKey setting to filter tags in frontmatter ([04d4010](https://github.com/Mara-Li/obsidian-my-thesaurus/commit/04d40100e56ca4c3eccb4b3153c2f48424b72cf0))
|
||||
|
||||
## [0.0.8](https://github.com/Mara-Li/obsidian-my-thesaurus/compare/0.0.7...0.0.8) (2025-02-21)
|
||||
### Features
|
||||
|
||||
* **csv:** allow more than 2 columns ([df3f1c6](https://github.com/Mara-Li/obsidian-my-thesaurus/commit/df3f1c6dd625fcb508a7c360e8f5c5d853839f44))
|
||||
* **tests:** add constants for mock translations and enhance CSV parsing tests ([1b50050](https://github.com/Mara-Li/obsidian-my-thesaurus/commit/1b50050700d386a35061d79c119cafb77ed04b87))
|
||||
* **tests:** add test for error handling with insufficient columns in CSV ([86fcbc2](https://github.com/Mara-Li/obsidian-my-thesaurus/commit/86fcbc23d02697177c8e0afa2676859be4826b55))
|
||||
* **utils:** add array comparison and missing elements functions for tag management ([25c6fab](https://github.com/Mara-Li/obsidian-my-thesaurus/commit/25c6fab0865458f45d8ce4e079f1dc9cbe601001))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **csv:** enhance CSV parsing with separator regex and column count validation ([5abff9c](https://github.com/Mara-Li/obsidian-my-thesaurus/commit/5abff9cf66edfb3122d568fe97d08a7b51d653bb))
|
||||
|
||||
## [0.0.7](https://github.com/Mara-Li/obsidian-my-thesaurus/compare/0.0.6...0.0.7) (2025-02-21)
|
||||
|
||||
## [0.0.6](https://github.com/Mara-Li/obsidian-my-thesaurus/compare/0.0.5...0.0.6) (2025-02-21)
|
||||
### Features
|
||||
|
||||
* **standardize:** allow to remove accents when reading the table ([25e7c38](https://github.com/Mara-Li/obsidian-my-thesaurus/commit/25e7c38de2662b5206bafa18dbc522de6c3d5c97))
|
||||
|
||||
## [0.0.5](https://github.com/Mara-Li/obsidian-my-thesaurus/compare/0.0.4...0.0.5) (2025-02-18)
|
||||
### Bug Fixes
|
||||
|
||||
* tags & tag lost ([aa3b8ef](https://github.com/Mara-Li/obsidian-my-thesaurus/commit/aa3b8ef598ce977eaa3e015b72cee07572c6a3d3))
|
||||
|
||||
## [0.0.4](https://github.com/Mara-Li/obsidian-my-thesaurus/compare/0.0.3...0.0.4) (2025-02-09)
|
||||
### Bug Fixes
|
||||
|
||||
* throw error when the path to the thesaurus is empty ([77b02f0](https://github.com/Mara-Li/obsidian-my-thesaurus/commit/77b02f04620bc8e88666146e84a85fb662c6a181))
|
||||
|
||||
## [0.0.3](https://github.com/Mara-Li/obsidian-my-thesaurus/compare/0.0.2...0.0.3) (2025-02-08)
|
||||
### Features
|
||||
|
||||
* allow markdown table ([c253daf](https://github.com/Mara-Li/obsidian-my-thesaurus/commit/c253dafb0e525eff4e1a5627baae010c37f7c67d))
|
||||
|
||||
## [0.0.2](https://github.com/Mara-Li/obsidian-my-thesaurus/compare/0.0.1...0.0.2) (2025-02-08)
|
||||
|
||||
## 0.0.1 (2025-02-08)
|
||||
|
|
|
|||
56
README.fr.md
56
README.fr.md
|
|
@ -1,22 +1,64 @@
|
|||
# My Thesaurus
|
||||
|
||||
Ajoute automatiquement des tags en fonction d’un thésaurus au format CSV.
|
||||
Ajoute automatiquement des tags en fonction d’un thésaurus au format CSV ou d'une table Markdown.
|
||||
|
||||
Le plugin cherche le mot ou la suite de mots dans l'entête (propriétés), dans le corps et le titre de la note. Le mot recherché le sera :
|
||||
- Strictement (sans troncature à droite ou gauche),
|
||||
- Insensible à la casse (majuscule ou minuscules)
|
||||
- Sensible aux accents (paramétrable)
|
||||
|
||||
> [!TIP]
|
||||
> `jeu` trouve `JEU` ou `JEu`
|
||||
> `jeu de carte` trouve `Jeu De Carte` mais pas `jeux de cartes`
|
||||
> `jeu` ne trouvera ni `jeux`, `enjeu` ou `rajeunir`
|
||||
> `rôle` ne trouvera pas `role`
|
||||
> `role` ne trouvera pas `rôle`
|
||||
|
||||
## 📚 Format du Thésaurus
|
||||
|
||||
Le thésaurus doit être un tableau avec au moins deux colonnes : une pour les termes et une pour les synonymes. La première ligne doit contenir les en-têtes de colonnes.
|
||||
|
||||
> [!TIP]
|
||||
> Le thésaurus peut être un fichier CSV comme ceci :
|
||||
> ```csv
|
||||
> Terme,Synonyme
|
||||
> ordinateur,PC
|
||||
> ordinateur,machine
|
||||
> ordinateur,vélo
|
||||
> ```
|
||||
>
|
||||
> Ou en format Markdown :
|
||||
> ```markdown
|
||||
> | Terme | Synonyme |
|
||||
> |------------|---------------|
|
||||
> | ordinateur | PC |
|
||||
> | ordinateur | machine |
|
||||
> | ordinateur | processeur |
|
||||
> ```
|
||||
|
||||
> [!NOTE]
|
||||
> Si vous choisissez d'utiliser un fichier Markdown, la table Markdown doit être le seul contenu du fichier.
|
||||
> Cependant, vous pouvez avoir un entête YAML (propriété) au début du fichier.
|
||||
|
||||
## ⚙️ Paramètres
|
||||
|
||||
1. <ins>Séparateur</ins> : Le séparateur utilisé dans le fichier CSV. Les séparateurs pris en charge sont `,`, `;`, `|` et `tabulation`.
|
||||
2. <ins>Colonne cible</ins> : Le titre de la colonne du fichier CSV contenant les tags :
|
||||
- **Terme** : La colonne contient les tags qui doivent être ajoutés par le plugin.
|
||||
- **Synonymes** : La colonne contient les synonymes qui doivent être reconnus par le plugin.
|
||||
3. <ins>Thésaurus</ins> : Chemin du fichier contenant le thésaurus. Ce fichier doit être un CSV et être stocké dans votre coffre (vault). Vous pouvez cliquer sur le bouton `save` pour vérifier que le fichier est valide (chemin correct, fichier existant et bien formé).
|
||||
1. <ins>Thésaurus</ins> : Chemin du fichier contenant le thésaurus. Ce fichier doit être un CSV et être stocké dans votre coffre (vault). Vous pouvez cliquer sur le bouton `save` pour vérifier que le fichier est valide (chemin correct, fichier existant et bien formé).
|
||||
|
||||
> [!WARNING]
|
||||
> Si, pour une raison quelconque, le fichier est déplacé, vous devrez mettre à jour son chemin dans les paramètres ! Il ne sera pas mis à jour automatiquement.
|
||||
|
||||
2. <ins>Séparateur</ins> : Le séparateur utilisé dans le fichier CSV. Les séparateurs pris en charge sont `,`, `;`, `|` et `tabulation`.
|
||||
3. <ins>Colonne cible</ins> : Le titre de la colonne du fichier CSV contenant les tags :
|
||||
- **Terme** : Nom de la colonne contenant les **tags à ajouter**.
|
||||
- **Synonymes** : Nom de la colonne des mots à **rechercher** dans le fichier (contenu et titre).
|
||||
4. <ins>Chemins inclus</ins> : Les fichiers situés dans ces dossiers seront pris en compte lors de l'exécution de la commande `My Thesaurus: Analyser tous les fichiers`. Vous pouvez séparer les chemins par une virgule, un point-virgule ou un saut de ligne. De plus, les expressions régulières (regex) sont prises en charge.
|
||||
|
||||
> [!TIP]
|
||||
> Vous pouvez inclure tous les fichiers de votre coffre en utilisant `.*` comme chemin !
|
||||
> Vous pouvez inclure tous les fichiers de votre coffre en utilisant `.*` comme chemin ! Cette valeur est utilisés par défaut.
|
||||
5. <ins>Exclure des termes</ins> : Permet d'exclure des termes en utilisant une clé de propriété.
|
||||
- **Clé de propriété** : La clé à rechercher dans les propriétés, par défaut `exclude_term`.
|
||||
- **Auto-nettoyage** : Si activé, le plugin nettoiera les tags déjà existants en supprimant les termes exclus.
|
||||
6. <ins>Supprimer les accents</ins> : Si activé, les accents seront supprimés lors de la reconnaissance de synonymes. Permet de faire correspondre `rôle` à `role` (et vice-versa).
|
||||
|
||||
## 📝 Utilisation
|
||||
|
||||
|
|
|
|||
96
README.md
96
README.md
|
|
@ -1,54 +1,100 @@
|
|||
# My Thesaurus
|
||||
# My Thesaurus
|
||||
-> [French README](./README.fr.md)
|
||||
|
||||
Automagically adds tags based on a thesaurus CSV.
|
||||
Automagically adds tags based on a thesaurus CSV or a Markdown table.
|
||||
|
||||
-> [French version](./README.fr.md)
|
||||
The plugin searches for the word or series of words in the header (properties), body and title of the note. The word searched for will be :
|
||||
- Strictly (without right or left truncation),
|
||||
- Case-insensitive (upper or lower case)
|
||||
- Accent-sensitive (configurable)
|
||||
|
||||
## ⚙️ Settings
|
||||
> [!TIP]
|
||||
> - `game` finds `GAME` or `GamE`.
|
||||
> - `card game` finds `Card Game` but not `cards games`
|
||||
> - `game` won't find `games`, `gaming` or `gameplay`
|
||||
> - `cafe` will not find `café`.
|
||||
> - `café` will not find `cafe`.
|
||||
|
||||
1. <ins>Separator</ins>: The separator used in the CSV file. Supported separators are `,`, `;`, `|`, and `tabulation`.
|
||||
2. <ins>Column title</ins>: The title of the column in the CSV file that contains the tags:
|
||||
- **Term**: The column contains the tags that must be set by the plugin.
|
||||
- **Synonyms**: The column contains the synonyms that must be recognized by the plugin.
|
||||
3. <ins>Thesaurus</ins>: Path to the file containing the thesaurus. The file must be a CSV file and must be in your vault. You can click on the `save` button to ensure the file is valid (the path is correct, the file exists, and it's well-formed).
|
||||
|
||||
> [!WARNING]
|
||||
> If, for some reason, the file is moved, you need to update the path in the settings! It won't be updated automatically.
|
||||
## 📚 Thesaurus Format
|
||||
|
||||
5. <u>Included paths</u>: Files in these folders will be included when using the command `My Thesaurus: Parse all files`. You can separate the paths with a comma, semicolon, or newline. Moreover, regex is supported.
|
||||
The thesaurus must be a table with at last two columns: one for the terms and one for the synonyms. The first row must contain the column headers.
|
||||
|
||||
> [!TIP]
|
||||
> You can include all the files in your vault by using `.*` as the path!
|
||||
> The thesaurus can be a CSV file like this:
|
||||
> ```csv
|
||||
> Tag,Synonym
|
||||
> computer,PC
|
||||
> computer,machine
|
||||
> computer,bike
|
||||
> ```
|
||||
>
|
||||
> Or in Markdown format:
|
||||
> ```markdown
|
||||
> | Tag | Synonym |
|
||||
> |-----------|-----------|
|
||||
> | computer | PC |
|
||||
> | computer | machine |
|
||||
> | computer | processor |
|
||||
> ```
|
||||
|
||||
## 📝 Usage
|
||||
> [!NOTE]
|
||||
> If you choose to use a markdown file, the Markdown table must be the only content in the file.
|
||||
> But, you can have a frontmatter at the beginning of the file.
|
||||
|
||||
The plugin has two commands:
|
||||
You can have as many synonyms as you want for a term, but you need to duplicate the term in the first column. Moreover, a synonym can be associated with multiple terms, and the synonym can equal to the term itself.
|
||||
|
||||
1. <ins>My Thesaurus: Parse all files</ins>: This command will parse all the files in the included paths and add the tags based on the thesaurus. At the end, a message will be displayed with the results, including:
|
||||
- **Errors**: The files that encountered an error and the specific error message.
|
||||
- **Skipped files**: The files that have been skipped because no synonym was found.
|
||||
- **Success**: The files that have been successfully parsed, along with the tags that have been added.
|
||||
2. <ins>My Thesaurus: Parse current file</ins>: This command will parse the current file and add the tags based on the thesaurus.
|
||||
## ⚙️ Settings
|
||||
|
||||
1. <ins>Thesaurus</ins>: Path to the file containing the thesaurus. The file must be a CSV file and must be in your vault. You can click on the `save` button to ensure the file is valid (the path is correct, the file exists, and it's well-formed).
|
||||
|
||||
> [!CAUTION]
|
||||
> If, for some reason, the file is moved, you need to update the path in the settings! It won't be updated automatically.
|
||||
|
||||
2. <ins>Separator</ins>: The separator used in the CSV file. Supported separators are `,`, `;`, `|`, `tabulation` and `markdown`. See the note above for the markdown separator.
|
||||
3. <ins>Column title</ins>: The title of the column in the CSV file that contains the tags:
|
||||
- **Term**: Name of the tag column **to be added**
|
||||
- **Synonyms**: Name of the column of **words to be searched in the file** (contents and file title).
|
||||
4. <ins>Included paths</ins>: Files in these folders will be included when using the command `My Thesaurus: Parse all files`. You can separate the paths with a comma, semicolon, or newline. Moreover, regex is supported.
|
||||
|
||||
> [!TIP]
|
||||
> You can include all the files in your vault by using `.*` as the path!
|
||||
|
||||
5. <ins>Excluded terms</ins>: Allow to exclude term using a frontmatter key. Include two settings:
|
||||
- **Frontmatter key**: The key to search in the frontmatter, by default `exclude_term`
|
||||
- **Auto-clean**: If enabled, the plugin will clean the already existing tags with removing the excluded terms.
|
||||
6. <ins>Remove accents</ins>: If enabled, accents will be suppressed when searching for synonyms. Allows `café` to be recognized by `cafe` (and vice versa).
|
||||
|
||||
|
||||
## 📝 Usage
|
||||
|
||||
The plugin has two commands:
|
||||
|
||||
1. <ins>My Thesaurus: Parse all files</ins>: This command will parse all the files in the included paths and add the tags based on the thesaurus. At the end, a message will be displayed with the results, including:
|
||||
- **Errors**: The files that encountered an error and the specific error message.
|
||||
- **Skipped files**: The files that have been skipped because no synonym was found.
|
||||
- **Success**: The files that have been successfully parsed, along with the tags that have been added.
|
||||
2. <ins>My Thesaurus: Parse current file</ins>: This command will parse the current file and add the tags based on the thesaurus.
|
||||
|
||||
## 📥 Installation
|
||||
|
||||
- [ ] From Obsidian's community plugins
|
||||
- [x] Using BRAT with `https://github.com/Mara-Li/`
|
||||
- [x] From the release page:
|
||||
- [x] From the release page:
|
||||
- Download the latest release
|
||||
- Unzip `my-thesaurus.zip` in `.obsidian/plugins/` path
|
||||
- In Obsidian settings, reload the plugin
|
||||
- Enable the plugin
|
||||
|
||||
|
||||
### 🎼 Languages
|
||||
|
||||
- [x] English
|
||||
- [x] French
|
||||
|
||||
To add a translation:
|
||||
|
||||
1. Fork the repository
|
||||
2. Add the translation in the `src/i18n/locales` folder with the name of the language (ex: `fr.json`).
|
||||
2. Add the translation in the `src/i18n/locales` folder with the name of the language (ex: `fr.json`).
|
||||
- You can get your locale language from Obsidian using [obsidian translation](https://github.com/obsidianmd/obsidian-translations) or using the commands (in templater for example) : `<% tp.obsidian.moment.locale() %>`
|
||||
- Copy the content of the [`en.json`](./src/i18n/locales/en.json) file in the new file
|
||||
- Translate the content
|
||||
|
|
@ -56,3 +102,7 @@ To add a translation:
|
|||
- Add `import * as <lang> from "./locales/<lang>.json";`
|
||||
- Edit the `ressource` part with adding : `<lang> : {translation: <lang>}`
|
||||
|
||||
---
|
||||
# Credits
|
||||
|
||||
- Inspired by [pmartinolli/MyThesaurus](https://github.com/pmartinolli/MyThesaurus)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"id": "my-thesaurus",
|
||||
"name": "My Thesaurus",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.10",
|
||||
"minAppVersion": "1.5.12",
|
||||
"description": "Automagically adds tags based on a simple csv file.",
|
||||
"description": "Automagically adds tags based on a simple csv file or a Markdown table.",
|
||||
"author": "mara-li",
|
||||
"authorUrl": "https//github.com/mara-li",
|
||||
"isDesktopOnly": false
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"id": "my-thesaurus",
|
||||
"name": "My Thesaurus",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.10",
|
||||
"minAppVersion": "1.5.12",
|
||||
"description": "Automagically adds tags based on a simple csv file.",
|
||||
"description": "Automagically adds tags based on a simple csv file or a Markdown table.",
|
||||
"author": "mara-li",
|
||||
"authorUrl": "https//github.com/mara-li",
|
||||
"authorUrl": "https://github.com/mara-li",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "my-thesaurus",
|
||||
"version": "0.0.2",
|
||||
"description": "Add automagically tags based on a thesaurus CSV.",
|
||||
"version": "0.0.10",
|
||||
"description": "Add automagically tags based on a thesaurus (CSV or markdown table).",
|
||||
"main": "main.js",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
|
@ -10,8 +10,8 @@
|
|||
"dev:prod": "node esbuild.config.mjs --vault",
|
||||
"dev": "node esbuild.config.mjs",
|
||||
"export": "node esbuild.config.mjs --production --vault",
|
||||
"bump": "dotenv -- node commit-and-tag-version.mjs",
|
||||
"postbump": "dotenv -- if-env SKIP_POST_BUMP=true && echo skip bump || git push --follow-tags origin master",
|
||||
"bump": "node commit-and-tag-version.mjs",
|
||||
"postbump": "git push --follow-tags origin master",
|
||||
"predeploy": "bun run bump",
|
||||
"deploy": "pnpm run export",
|
||||
"lint": "pnpm biome format --write src/"
|
||||
|
|
|
|||
|
|
@ -1,67 +1,87 @@
|
|||
{
|
||||
"command": {
|
||||
"active": "Add thesaurus tags to active file",
|
||||
"all": "Parse all"
|
||||
},
|
||||
"error": {
|
||||
"csv": {
|
||||
"columns": "The columns doesn't correspond with the settings.",
|
||||
"header": "You can have only two column in your thesaurus. You have: {{- len}} columns.",
|
||||
"malformed": "Your CSV is malformed: {{- len}} columns found.",
|
||||
"separator": "The specified separator is invalid. You seems to use \"{{- sep}}\" as separator."
|
||||
},
|
||||
"file": {
|
||||
"empty": "The file {{- file}} is empty!"
|
||||
},
|
||||
"noFiles": "No files to parse found!",
|
||||
"noPaths": "No file paths have been included in the parameters.",
|
||||
"notFound": "The thesaurus is not found at the provided path ({{- path}})."
|
||||
},
|
||||
"modal": {
|
||||
"errors": "Errors",
|
||||
"skips": "Skipped",
|
||||
"success": "Success",
|
||||
"title": "MyThesaurus - Results"
|
||||
},
|
||||
"notice": {
|
||||
"done": "Done !",
|
||||
"loading": "Loading...",
|
||||
"start": "Start treatment..."
|
||||
},
|
||||
"settings": {
|
||||
"columns": {
|
||||
"synonyms": {
|
||||
"desc": "These values will be recognized in the file content and the corresponding term will be applied.",
|
||||
"title": "Synonym"
|
||||
},
|
||||
"term": {
|
||||
"desc": "This value will be set as a tag in your properties.",
|
||||
"title": "Term"
|
||||
},
|
||||
"title": "Column title"
|
||||
},
|
||||
"includedPaths": {
|
||||
"desc": "Includes files from its paths when using the global command.",
|
||||
"regex": "Regex are supported.",
|
||||
"separate": "Separate paths with coma, semicolon, spaces or newlines.",
|
||||
"title": "Included paths"
|
||||
},
|
||||
"separator": {
|
||||
"desc": "CSV column separator.",
|
||||
"tab": "Tabulation",
|
||||
"title": "Separator"
|
||||
},
|
||||
"thesaurusPath": {
|
||||
"desc": "Path to your CSV thesaurus (in your vault).",
|
||||
"error": "The provided file is not valide.",
|
||||
"placeholder": "mon_thesaurus.csv",
|
||||
"success": "The provided file is correct!",
|
||||
"title": "Thesaurus",
|
||||
"verify": "Verify validity of the file"
|
||||
}
|
||||
},
|
||||
"success": {
|
||||
"none": "No tag to add!",
|
||||
"title": "The tags has been added:"
|
||||
}
|
||||
}
|
||||
"command": {
|
||||
"active": "Add thesaurus tags to active file",
|
||||
"all": "Parse all"
|
||||
},
|
||||
"error": {
|
||||
"csv": {
|
||||
"columns": "The columns doesn't correspond with the settings.",
|
||||
"header": "You can have only two column in your thesaurus. You have: {{- len}} columns.",
|
||||
"malformed": "Your CSV is malformed: there is no column.",
|
||||
"separator": "The specified separator is invalid. You seems to use \"{{- sep}}\" as separator."
|
||||
},
|
||||
"file": {
|
||||
"empty": "The file {{- file}} is empty!"
|
||||
},
|
||||
"noFiles": "No files to parse found!",
|
||||
"noPath": "The path to the thesaurus is empty!",
|
||||
"noPaths": "No file paths have been included in the parameters.",
|
||||
"notFound": "The thesaurus is not found at the provided path ({{- path}})."
|
||||
},
|
||||
"modal": {
|
||||
"errors": "Errors",
|
||||
"skips": "Skipped",
|
||||
"success": "Success",
|
||||
"title": "MyThesaurus - Results"
|
||||
},
|
||||
"notice": {
|
||||
"done": "Done !",
|
||||
"loading": "Loading...",
|
||||
"start": "Start treatment..."
|
||||
},
|
||||
"settings": {
|
||||
"columns": {
|
||||
"synonyms": {
|
||||
"desc": "This value is the name of the column of words to be searched in the file (contents and title).",
|
||||
"title": "Synonym"
|
||||
},
|
||||
"term": {
|
||||
"desc": "This value is the name of the tag column to be added.",
|
||||
"title": "Term"
|
||||
},
|
||||
"title": "Column title"
|
||||
},
|
||||
"exclude": {
|
||||
"clean": {
|
||||
"desc": "Automatically deletes existing tags if a term is excluded.",
|
||||
"title": "Auto-clean"
|
||||
},
|
||||
"desc": "Allow to exclude term using a property key.",
|
||||
"key": "Frontmatter key",
|
||||
"title": "Excluding terms"
|
||||
},
|
||||
"includedPaths": {
|
||||
"desc": "Includes files from its paths when using the global command.",
|
||||
"regex": "Regex are supported.",
|
||||
"separate": "Separate paths with coma, semicolon, spaces or newlines.",
|
||||
"title": "Included paths"
|
||||
},
|
||||
"other": "Other",
|
||||
"removeAccents": {
|
||||
"by": "like",
|
||||
"cafe": "cafe",
|
||||
"café": "café",
|
||||
"desc": "Ignores accents during recognition: helps identify",
|
||||
"title": "Remove accents",
|
||||
"viceversa": "(and vice versa)"
|
||||
},
|
||||
"separator": {
|
||||
"desc": "CSV column separator.",
|
||||
"md": "Markdown table",
|
||||
"tab": "Tabulation",
|
||||
"title": "Separator"
|
||||
},
|
||||
"thesaurusPath": {
|
||||
"desc": "Path to your CSV thesaurus (in your vault).",
|
||||
"error": "The provided file is not valide.",
|
||||
"placeholder": "mon_thesaurus.csv",
|
||||
"success": "The provided file is correct!",
|
||||
"title": "Thesaurus",
|
||||
"verify": "Verify validity of the file"
|
||||
}
|
||||
},
|
||||
"success": {
|
||||
"none": "No tag to add!",
|
||||
"title": "The tags has been added:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,13 +7,14 @@
|
|||
"csv": {
|
||||
"columns": "Les colonnes du CSV fourni ne correspondent pas avec les colonnes des paramètres.",
|
||||
"header": "Vous ne pouvez avoir que deux colonnes dans votre thésaurus. Vous avez {{- len}} colonnes.",
|
||||
"malformed": "Votre CSV est mal formé : {{- len}} colonnes trouvées.",
|
||||
"malformed": "Votre CSV est mal formé : aucune colonne n'a été trouvées.",
|
||||
"separator": "Le séparateur spécifié n'est pas le bon. Vous semblez utiliser \"{{- sep}}\" comme séparateur."
|
||||
},
|
||||
"file": {
|
||||
"empty": "Le fichier {{- file}} est vide !"
|
||||
},
|
||||
"noFiles": "Aucun fichier n'a été trouvé !",
|
||||
"noPath": "Le chemin vers le thésaurus est vide !",
|
||||
"noPaths": "Aucun chemins de fichiers n'a été inclus dans les paramètres.",
|
||||
"notFound": "Le thésaurus est introuvable au chemin fourni ({{-path}})."
|
||||
},
|
||||
|
|
@ -31,23 +32,42 @@
|
|||
"settings": {
|
||||
"columns": {
|
||||
"synonyms": {
|
||||
"desc": "Ces valeurs seront reconnus dans le contenu du fichier et le terme correspondant sera appliqué.",
|
||||
"desc": "Cette valeur est le nom de la colonne des mots à chercher dans le fichier (contenu et titre).",
|
||||
"title": "Synonymes"
|
||||
},
|
||||
"term": {
|
||||
"desc": "Cette valeur sera ajouté comme tag dans vos propriétés.",
|
||||
"desc": "Cette valeur est le nom de la colonne des tags à ajouter.",
|
||||
"title": "Terme"
|
||||
},
|
||||
"title": "Colonne cible"
|
||||
},
|
||||
"exclude": {
|
||||
"clean": {
|
||||
"desc": "Supprime automatiquement les tags déjà existant si un terme est exclu.",
|
||||
"title": "Auto-nettoyage"
|
||||
},
|
||||
"desc": "Permet d'exclure des termes en utilisant une clé de propriétés",
|
||||
"key": "Clé de propriétés",
|
||||
"title": "Exclure des termes"
|
||||
},
|
||||
"includedPaths": {
|
||||
"desc": "Inclus les fichiers depuis ses chemins lors d'utilisation de la commande globale.",
|
||||
"regex": "Les expressions régulières sont supportées.",
|
||||
"separate": "Séparer les chemins par une virgule, point virgule ou un retour à la ligne.",
|
||||
"title": "Chemins inclus"
|
||||
},
|
||||
"other": "Autres",
|
||||
"removeAccents": {
|
||||
"by": "comme",
|
||||
"cafe": "cafe",
|
||||
"café": "café",
|
||||
"desc": "Ignore les accents lors de la reconnaissance : permet d'identifier",
|
||||
"title": "Supprimer les accents",
|
||||
"viceversa": "(et inversement)"
|
||||
},
|
||||
"separator": {
|
||||
"desc": "Séparateur des colonnes du fichier CSV.",
|
||||
"md": "Markdown table",
|
||||
"tab": "Tabulation",
|
||||
"title": "Séparateur"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ export interface MyThesaurusSettings {
|
|||
includedPaths: string[];
|
||||
separator: Separator;
|
||||
columns: ColumnName;
|
||||
removeAccents: boolean;
|
||||
excludeTermKey: string;
|
||||
cleanExcludedTerms: boolean;
|
||||
}
|
||||
|
||||
export type ColumnName = {
|
||||
|
|
@ -21,17 +24,20 @@ export type ParseResult = {
|
|||
|
||||
export type ParseResults = ParseResult[];
|
||||
|
||||
export type Separator = ";" | "," | "\t" | "|";
|
||||
export type Separator = ";" | "," | "\t" | "|" | "md";
|
||||
|
||||
export type Thesaurus = Record<string, Set<string>>;
|
||||
export type Translation = TFunction<"translation", undefined>;
|
||||
|
||||
export const DEFAULT_SETTINGS: MyThesaurusSettings = {
|
||||
thesaurusPath: "",
|
||||
includedPaths: [],
|
||||
includedPaths: [".*"],
|
||||
separator: ";",
|
||||
columns: {
|
||||
term: "term",
|
||||
synonyms: "synonyms",
|
||||
},
|
||||
removeAccents: false,
|
||||
excludeTermKey: "exclude_term",
|
||||
cleanExcludedTerms: false,
|
||||
};
|
||||
|
|
|
|||
134
src/main.ts
134
src/main.ts
|
|
@ -1,5 +1,12 @@
|
|||
import i18next from "i18next";
|
||||
import { Notice, Plugin, TFile, sanitizeHTMLToDom } from "obsidian";
|
||||
import {
|
||||
type FrontMatterCache,
|
||||
Notice,
|
||||
Plugin,
|
||||
TFile,
|
||||
getFrontMatterInfo,
|
||||
sanitizeHTMLToDom,
|
||||
} from "obsidian";
|
||||
import { resources, translationLanguage } from "./i18n";
|
||||
import {
|
||||
DEFAULT_SETTINGS,
|
||||
|
|
@ -11,24 +18,64 @@ import {
|
|||
import { MyThesaurusSettingTab } from "./settings";
|
||||
import "uniformize";
|
||||
import { ResultModals } from "./modals";
|
||||
import { getTags, getThesaurus } from "./utils";
|
||||
import {
|
||||
areArraysEqual,
|
||||
findMissingElements,
|
||||
getKeysAsArray,
|
||||
getTags,
|
||||
getThesaurus,
|
||||
} from "./utils";
|
||||
|
||||
export default class MyThesaurus extends Plugin {
|
||||
settings!: MyThesaurusSettings;
|
||||
|
||||
async addTagsToNote(tags: string[], file: TFile) {
|
||||
const currentTags = this.app.metadataCache.getFileCache(file)?.tags || [];
|
||||
const newTags = [...new Set([...currentTags, ...tags])];
|
||||
if (newTags.length === 0) return;
|
||||
getFileTags(frontmatter: FrontMatterCache) {
|
||||
const tags = [];
|
||||
if (frontmatter.tags) tags.push(...getKeysAsArray(frontmatter.tags));
|
||||
if (frontmatter.tag) tags.push(...getKeysAsArray(frontmatter.tag));
|
||||
return tags;
|
||||
}
|
||||
|
||||
filterTerm(frontmatter: FrontMatterCache, tags: Set<string>) {
|
||||
const keyToFind = this.settings.excludeTermKey;
|
||||
if (keyToFind.trim().length === 0) return [...tags];
|
||||
if (frontmatter?.[keyToFind]) {
|
||||
const excludeList = getKeysAsArray(frontmatter[keyToFind]);
|
||||
for (const tag of excludeList) tags.delete(tag);
|
||||
}
|
||||
return [...tags];
|
||||
}
|
||||
|
||||
async addTagsToNote(
|
||||
tags: string[],
|
||||
file: TFile,
|
||||
currentTags: string[],
|
||||
frontmatter: FrontMatterCache
|
||||
): Promise<boolean> {
|
||||
if (areArraysEqual(tags, currentTags)) return false;
|
||||
const updatedTags = new Set([...currentTags, ...tags]);
|
||||
let newTags = [...updatedTags];
|
||||
if (this.settings.cleanExcludedTerms)
|
||||
newTags = this.filterTerm(frontmatter, updatedTags);
|
||||
if (newTags.length === 0) return false;
|
||||
await this.app.fileManager.processFrontMatter(file, (frontmatter) => {
|
||||
frontmatter.tags = newTags;
|
||||
if (frontmatter.tag) frontmatter.tag = newTags;
|
||||
else frontmatter.tags = newTags;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
notice(message: string | DocumentFragment, silent = false) {
|
||||
if (!silent) {
|
||||
new Notice(message);
|
||||
}
|
||||
if (!silent) new Notice(message);
|
||||
}
|
||||
|
||||
skip(file: TFile, silent = false): ParseResult {
|
||||
this.notice(i18next.t("success.none"), silent);
|
||||
return {
|
||||
file: file.basename,
|
||||
tags: [],
|
||||
type: "skip",
|
||||
};
|
||||
}
|
||||
|
||||
async parseFile(
|
||||
|
|
@ -48,26 +95,29 @@ export default class MyThesaurus extends Plugin {
|
|||
};
|
||||
}
|
||||
try {
|
||||
const tags = getTags(contents, thesaurus);
|
||||
if (tags.length > 0) {
|
||||
await this.addTagsToNote(tags, file);
|
||||
const successMsg = sanitizeHTMLToDom(
|
||||
`<span class="success">${i18next.t("success.title")}</span>${tags.map((tag) => `<li><code>${tag}</code></li>`).join("")}`
|
||||
);
|
||||
this.notice(successMsg, silent);
|
||||
return {
|
||||
file: file.basename,
|
||||
tags: tags,
|
||||
type: "success",
|
||||
};
|
||||
} else {
|
||||
this.notice(i18next.t("success.none"), silent);
|
||||
return {
|
||||
file: file.basename,
|
||||
tags: [],
|
||||
type: "skip",
|
||||
};
|
||||
}
|
||||
const frontmatter = this.app.metadataCache.getFileCache(file)?.frontmatter || {};
|
||||
const tags = getTags(contents, thesaurus, this.settings.removeAccents).concat(
|
||||
...getTags(file.basename, thesaurus, this.settings.removeAccents)
|
||||
);
|
||||
const tagsToAdd = this.filterTerm(frontmatter, new Set(tags));
|
||||
|
||||
if (tagsToAdd.length > 0) {
|
||||
const currentTags = this.getFileTags(frontmatter);
|
||||
const added = await this.addTagsToNote(tagsToAdd, file, currentTags, frontmatter);
|
||||
if (added) {
|
||||
const missingTags = findMissingElements(currentTags, tagsToAdd);
|
||||
if (missingTags.length === 0) return this.skip(file, silent);
|
||||
const successMsg = sanitizeHTMLToDom(
|
||||
`<span class="success">${i18next.t("success.title")}</span>${missingTags.map((tag) => `<li><code>${tag}</code></li>`).join("")}`
|
||||
);
|
||||
this.notice(successMsg, silent);
|
||||
return {
|
||||
file: file.basename,
|
||||
tags: tagsToAdd,
|
||||
type: "success",
|
||||
};
|
||||
} else return this.skip(file, silent);
|
||||
} else return this.skip(file, silent);
|
||||
} catch (error) {
|
||||
this.notice(
|
||||
sanitizeHTMLToDom(`<span class="errors">${(error as Error).message}</span>`),
|
||||
|
|
@ -83,27 +133,37 @@ export default class MyThesaurus extends Plugin {
|
|||
}
|
||||
|
||||
async readThesaurus() {
|
||||
if (this.settings.thesaurusPath.length === 0)
|
||||
throw new Error(i18next.t("error.noPath"));
|
||||
const thesaurusFile = this.app.vault.getAbstractFileByPath(
|
||||
this.settings.thesaurusPath
|
||||
);
|
||||
const ext = this.settings.separator === "md" ? "md" : "csv";
|
||||
if (
|
||||
!thesaurusFile ||
|
||||
!(thesaurusFile instanceof TFile) ||
|
||||
thesaurusFile.extension !== "csv"
|
||||
thesaurusFile.extension !== ext
|
||||
) {
|
||||
throw new Error(i18next.t("error.notFound"));
|
||||
}
|
||||
return await this.app.vault.read(thesaurusFile);
|
||||
let contents = await this.app.vault.read(thesaurusFile);
|
||||
if (ext === "md") {
|
||||
contents = contents.replace(/^\s*\n/gm, "");
|
||||
//remove the frontmatter
|
||||
const frontmatterInfo = getFrontMatterInfo(contents);
|
||||
|
||||
if (frontmatterInfo.exists) return contents.slice(frontmatterInfo.contentStart);
|
||||
return contents;
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
scanAllFiles(): TFile[] {
|
||||
const allMarkdownFiles = this.app.vault.getMarkdownFiles();
|
||||
console.log(allMarkdownFiles);
|
||||
const files = [];
|
||||
for (const filter of this.settings.includedPaths) {
|
||||
try {
|
||||
const regex = new RegExp(filter, "g");
|
||||
console.log(regex);
|
||||
files.push(...allMarkdownFiles.filter((file) => file.path.match(regex)));
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
|
@ -132,7 +192,8 @@ export default class MyThesaurus extends Plugin {
|
|||
await this.readThesaurus(),
|
||||
this.settings.separator,
|
||||
i18next.t,
|
||||
this.settings.columns
|
||||
this.settings.columns,
|
||||
this.settings.removeAccents
|
||||
);
|
||||
const results: ParseResults = [];
|
||||
const noticeBar = new Notice(
|
||||
|
|
@ -188,7 +249,8 @@ export default class MyThesaurus extends Plugin {
|
|||
thesaurusContent,
|
||||
this.settings.separator,
|
||||
i18next.t,
|
||||
this.settings.columns
|
||||
this.settings.columns,
|
||||
this.settings.removeAccents
|
||||
);
|
||||
this.parseFile(file, false, thesaurus);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
import { AbstractInputSuggest, type App, type TFile } from "obsidian";
|
||||
|
||||
export class FileSuggester extends AbstractInputSuggest<TFile> {
|
||||
ext: "csv" | "md" = "csv";
|
||||
constructor(
|
||||
private inputEl: HTMLInputElement,
|
||||
app: App,
|
||||
ext: "csv" | "md",
|
||||
private onSubmit: (value: TFile) => void
|
||||
) {
|
||||
super(app, inputEl);
|
||||
this.ext = ext;
|
||||
}
|
||||
|
||||
renderSuggestion(value: TFile, el: HTMLElement): void {
|
||||
|
|
@ -14,7 +17,9 @@ export class FileSuggester extends AbstractInputSuggest<TFile> {
|
|||
}
|
||||
|
||||
getSuggestions(query: string): TFile[] {
|
||||
const allFiles = this.app.vault.getFiles().filter((file) => file.extension === "csv");
|
||||
const allFiles = this.app.vault
|
||||
.getFiles()
|
||||
.filter((file) => file.extension === this.ext);
|
||||
return allFiles.filter((file) => file.path.subText(query));
|
||||
}
|
||||
|
||||
|
|
|
|||
165
src/settings.ts
165
src/settings.ts
|
|
@ -15,11 +15,13 @@ import { getThesaurus } from "./utils";
|
|||
export class MyThesaurusSettingTab extends PluginSettingTab {
|
||||
plugin: MyThesaurus;
|
||||
settings: MyThesaurusSettings;
|
||||
isInvalid: boolean;
|
||||
|
||||
constructor(app: App, plugin: MyThesaurus) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
this.settings = plugin.settings;
|
||||
this.isInvalid = false;
|
||||
}
|
||||
|
||||
display(): void {
|
||||
|
|
@ -28,19 +30,82 @@ export class MyThesaurusSettingTab extends PluginSettingTab {
|
|||
|
||||
containerEl.empty();
|
||||
|
||||
const thesaurusSetting = new Setting(containerEl)
|
||||
.setHeading()
|
||||
.setName(i18next.t("settings.thesaurusPath.title"))
|
||||
.setDesc(i18next.t("settings.thesaurusPath.desc"))
|
||||
.addSearch(async (cb) => {
|
||||
cb.setPlaceholder(i18next.t("settings.thesaurusPath.placeholder"));
|
||||
cb.setValue(this.settings.thesaurusPath);
|
||||
const ext = this.settings.separator === "md" ? "md" : "csv";
|
||||
new FileSuggester(cb.inputEl, this.app, ext, async (result) => {
|
||||
this.settings.thesaurusPath = result.path;
|
||||
await this.plugin.saveSettings();
|
||||
});
|
||||
//clean when the cb.clear() is called
|
||||
cb.clearButtonEl.addEventListener("click", async () => {
|
||||
this.settings.thesaurusPath = "";
|
||||
await this.plugin.saveSettings();
|
||||
});
|
||||
})
|
||||
.addExtraButton((button) => {
|
||||
button
|
||||
.setIcon("save")
|
||||
.setTooltip(i18next.t("settings.thesaurusPath.verify"))
|
||||
.onClick(async () => {
|
||||
const file = this.app.vault.getAbstractFileByPath(
|
||||
this.settings.thesaurusPath
|
||||
);
|
||||
if (!(file instanceof TFile)) {
|
||||
new Notice(i18next.t("settings.thesaurusPath.error"));
|
||||
this.isInvalid = true;
|
||||
this.display();
|
||||
return;
|
||||
}
|
||||
const content = await this.plugin.readThesaurus();
|
||||
try {
|
||||
getThesaurus(
|
||||
content,
|
||||
this.settings.separator,
|
||||
i18next.t,
|
||||
this.settings.columns,
|
||||
this.settings.removeAccents
|
||||
);
|
||||
this.isInvalid = false;
|
||||
this.display();
|
||||
} catch (e) {
|
||||
new Notice(
|
||||
sanitizeHTMLToDom(`<span class="error">${(e as Error).message}</span>`)
|
||||
);
|
||||
this.isInvalid = true;
|
||||
this.display();
|
||||
return;
|
||||
}
|
||||
new Notice(
|
||||
sanitizeHTMLToDom(
|
||||
`<span class="success">${i18next.t("settings.thesaurusPath.success")}</span>`
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
if (this.isInvalid) thesaurusSetting.setClass("is-invalid");
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(i18next.t("settings.separator.title"))
|
||||
.setDesc(i18next.t("settings.separator.desc"))
|
||||
.setClass("no-border")
|
||||
.addDropdown((dropdown) => {
|
||||
dropdown
|
||||
.addOption(";", ";")
|
||||
.addOption(",", ",")
|
||||
.addOption("\t", `${i18next.t("settings.separator.tab")}`)
|
||||
.addOption("|", "|")
|
||||
.addOption("md", `${i18next.t("settings.separator.md")}`)
|
||||
.setValue(this.settings.separator)
|
||||
.onChange(async (value) => {
|
||||
this.settings.separator = value as Separator;
|
||||
await this.plugin.saveSettings();
|
||||
this.display();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -48,6 +113,7 @@ export class MyThesaurusSettingTab extends PluginSettingTab {
|
|||
|
||||
new Setting(containerEl)
|
||||
.setName(i18next.t("settings.columns.term.title"))
|
||||
.setClass("no-border")
|
||||
.setDesc(i18next.t("settings.columns.term.desc"))
|
||||
.addText((text) => {
|
||||
text.setValue(this.settings.columns.term).onChange(async (value) => {
|
||||
|
|
@ -57,6 +123,7 @@ export class MyThesaurusSettingTab extends PluginSettingTab {
|
|||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setClass("no-border")
|
||||
.setName(i18next.t("settings.columns.synonyms.title"))
|
||||
.setDesc(i18next.t("settings.columns.synonyms.desc"))
|
||||
.addText((text) => {
|
||||
|
|
@ -64,65 +131,67 @@ export class MyThesaurusSettingTab extends PluginSettingTab {
|
|||
this.settings.columns.synonyms = value;
|
||||
await this.plugin.saveSettings();
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setHeading()
|
||||
.setName(i18next.t("settings.thesaurusPath.title"))
|
||||
.setDesc(i18next.t("settings.thesaurusPath.desc"))
|
||||
.addSearch(async (cb) => {
|
||||
cb.setPlaceholder(i18next.t("settings.thesaurusPath.placeholder"));
|
||||
cb.setValue(this.settings.thesaurusPath);
|
||||
new FileSuggester(cb.inputEl, this.app, async (result) => {
|
||||
this.settings.thesaurusPath = result.path;
|
||||
await this.plugin.saveSettings();
|
||||
});
|
||||
})
|
||||
.addExtraButton((button) => {
|
||||
button
|
||||
.setIcon("save")
|
||||
.setTooltip(i18next.t("settings.thesaurusPath.verify"))
|
||||
.onClick(async () => {
|
||||
const file = this.app.vault.getAbstractFileByPath(
|
||||
this.settings.thesaurusPath
|
||||
);
|
||||
if (!(file instanceof TFile)) {
|
||||
new Notice(i18next.t("settings.thesaurusPath.error"));
|
||||
return;
|
||||
}
|
||||
const content = await this.app.vault.read(file);
|
||||
try {
|
||||
getThesaurus(
|
||||
content,
|
||||
this.settings.separator,
|
||||
i18next.t,
|
||||
this.settings.columns
|
||||
);
|
||||
} catch (e) {
|
||||
new Notice((e as Error).message);
|
||||
this.settings.thesaurusPath = "";
|
||||
await this.plugin.saveSettings();
|
||||
this.display();
|
||||
return;
|
||||
}
|
||||
new Notice(i18next.t("settings.thesaurusPath.success"));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(i18next.t("settings.includedPaths.title"))
|
||||
.setHeading()
|
||||
.setClass("border-top")
|
||||
.setDesc(
|
||||
sanitizeHTMLToDom(
|
||||
`${i18next.t("settings.includedPaths.desc")}<br>${i18next.t("settings.includedPaths.separate")}<br>${i18next.t("settings.includedPaths.regex")}`
|
||||
)
|
||||
)
|
||||
.addTextArea((text) => {
|
||||
text.setValue(this.settings.includedPaths.join("\n")).onChange(async (value) => {
|
||||
this.settings.includedPaths = value
|
||||
.split(/[\n,;]+/)
|
||||
.map((path) => path.trim())
|
||||
.filter((path) => path.length > 0);
|
||||
text
|
||||
.setPlaceholder(".*")
|
||||
.setValue(this.settings.includedPaths.join("\n"))
|
||||
.onChange(async (value) => {
|
||||
this.settings.includedPaths = value
|
||||
.split(/[\n,;]+/)
|
||||
.map((path) => path.trim())
|
||||
.filter((path) => path.length > 0);
|
||||
await this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(i18next.t("settings.exclude.title"))
|
||||
.setHeading()
|
||||
.setDesc(i18next.t("settings.exclude.desc"));
|
||||
new Setting(containerEl)
|
||||
.setName(i18next.t("settings.exclude.key"))
|
||||
.setClass("no-border")
|
||||
.addText((text) => {
|
||||
text.setValue(this.settings.excludeTermKey).onChange(async (value) => {
|
||||
this.settings.excludeTermKey = value;
|
||||
await this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
new Setting(containerEl)
|
||||
.setName(i18next.t("settings.exclude.clean.title"))
|
||||
.setDesc(i18next.t("settings.exclude.clean.desc"))
|
||||
.setClass("no-border")
|
||||
.addToggle((toggle) => {
|
||||
toggle.setValue(this.settings.cleanExcludedTerms).onChange(async (value) => {
|
||||
this.settings.cleanExcludedTerms = value;
|
||||
await this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl).setHeading().setName(i18next.t("settings.other"));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(i18next.t("settings.removeAccents.title"))
|
||||
.setClass("no-border")
|
||||
.setDesc(
|
||||
sanitizeHTMLToDom(
|
||||
`${i18next.t("settings.removeAccents.desc")}<code>"${i18next.t("settings.removeAccents.café")}"</code>${i18next.t("settings.removeAccents.by")}<code>"${i18next.t("settings.removeAccents.cafe")}"</code>${i18next.t("settings.removeAccents.viceversa")}.`
|
||||
)
|
||||
)
|
||||
.addToggle((toggle) => {
|
||||
toggle.setValue(this.settings.removeAccents).onChange(async (value) => {
|
||||
this.settings.removeAccents = value;
|
||||
await this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -57,3 +57,21 @@
|
|||
.my-thesaurus.modal-title {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.my-thesaurus .is-invalid input {
|
||||
border-color: var(--text-error);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.my-thesaurus .no-border {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.my-thesaurus .setting-item-heading {
|
||||
--font-ui-medium: 1.25rem;
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.my-thesaurus .border-top {
|
||||
border-top: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,20 @@
|
|||
import { getThesaurus } from "./parse_csv";
|
||||
import { getHeader, getThesaurus } from "./parse_csv";
|
||||
import { getTags } from "./parse_file";
|
||||
|
||||
export { getThesaurus, getTags };
|
||||
export { getThesaurus, getTags, getHeader };
|
||||
|
||||
export function areArraysEqual<T>(arr1?: T[], arr2?: T[]): boolean {
|
||||
if ((arr1 && !arr2) || (!arr1 && arr2) || (!arr1 && !arr2)) return false;
|
||||
if (arr1 && arr2)
|
||||
return arr1.length === arr2.length && arr1.every((val, i) => val === arr2[i]);
|
||||
return false;
|
||||
}
|
||||
export function findMissingElements<T>(currentTags: T[], tags: T[]): T[] {
|
||||
const currentSet = new Set(currentTags);
|
||||
return tags.filter((tag) => !currentSet.has(tag));
|
||||
}
|
||||
|
||||
export function getKeysAsArray(tag: string | string[]) {
|
||||
if (!Array.isArray(tag)) return [tag];
|
||||
return tag;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,70 +1,71 @@
|
|||
import type { ColumnName, Separator, Thesaurus, Translation } from "../interfaces";
|
||||
|
||||
const separatorRegex = /[,;\t\|]/;
|
||||
|
||||
function verifySeparator(header: string, sep: Separator): boolean {
|
||||
return header.includes(sep);
|
||||
}
|
||||
|
||||
function searchSeparator(header: string): Separator {
|
||||
return header.match(/[,;\t\|]/)?.[0] as Separator;
|
||||
return header.match(separatorRegex)?.[0] as Separator;
|
||||
}
|
||||
|
||||
function getColumn(
|
||||
function countColumn(header: string) {
|
||||
return header.split(separatorRegex).length;
|
||||
}
|
||||
|
||||
export function getColumn(
|
||||
header: string[],
|
||||
columnNames: ColumnName,
|
||||
ln: Translation
|
||||
): { indexKey: number; indexSynonyms: number } {
|
||||
const col1 = header[0].trim().standardize();
|
||||
const col2 = header[1].trim().standardize();
|
||||
let indexKey = 1;
|
||||
let indexSynonyms = 0;
|
||||
const standardized = structuredClone(columnNames);
|
||||
standardized.term = standardized.term.standardize();
|
||||
standardized.synonyms = standardized.synonyms.standardize();
|
||||
if (
|
||||
(col1 !== standardized.term && col1 !== standardized.synonyms) ||
|
||||
(col2 !== standardized.term && col2 !== standardized.synonyms)
|
||||
) {
|
||||
throw new Error(ln("error.csv.columns"));
|
||||
}
|
||||
if (col1 === standardized.term) {
|
||||
indexKey = 0;
|
||||
indexSynonyms = 1;
|
||||
}
|
||||
const indexSynonyms = header.findIndex(
|
||||
(x) => x.standardize() === standardized.synonyms
|
||||
);
|
||||
const indexKey = header.findIndex((x) => x.standardize() === standardized.term);
|
||||
if (indexKey === -1 || indexSynonyms === -1) throw new Error(ln("error.csv.columns"));
|
||||
return { indexKey, indexSynonyms };
|
||||
}
|
||||
|
||||
export function getHeader(fileContent: string[], separator: Separator) {
|
||||
return fileContent[0]
|
||||
.split(separator)
|
||||
.filter((col) => col.trim() !== "")
|
||||
.map((col) => col.trim());
|
||||
}
|
||||
|
||||
export function getThesaurus(
|
||||
fileContent: string,
|
||||
separator: Separator,
|
||||
ln: Translation,
|
||||
columnNames: ColumnName
|
||||
columnNames: ColumnName,
|
||||
standardize?: boolean
|
||||
) {
|
||||
//verify if they are only two columns
|
||||
if (fileContent.trim().length === 0) throw new Error(ln("error.csv.malformed"));
|
||||
|
||||
const isMd = separator === "md";
|
||||
separator = isMd ? "|" : separator;
|
||||
const lines = fileContent.split("\n");
|
||||
if (countColumn(lines[0]) <= 1) throw new Error(ln("error.csv.malformed"));
|
||||
if (!verifySeparator(lines[0], separator))
|
||||
throw new Error(ln("error.csv.separator", { sep: searchSeparator(lines[0]) }));
|
||||
|
||||
const header = lines[0].split(separator);
|
||||
if (header.length !== 2) {
|
||||
throw new Error(ln("error.csv.header", { len: header.length }));
|
||||
}
|
||||
const header = getHeader(lines, separator);
|
||||
if (header.length <= 1) throw new Error(ln("error.csv.malformed"));
|
||||
const { indexKey, indexSynonyms } = getColumn(header, columnNames, ln);
|
||||
const thesaurus: Thesaurus = {};
|
||||
for (const line of lines.slice(1).filter((line) => line.trim() !== "")) {
|
||||
const columns = line.split(separator);
|
||||
|
||||
if (columns.length > 0 && columns.length !== 2)
|
||||
throw new Error(ln("error.csv.malformed", { len: columns.length }));
|
||||
const lineSlice = isMd ? 2 : 1;
|
||||
for (const line of lines.slice(lineSlice).filter((line) => line.trim() !== "")) {
|
||||
const columns = line.split(separator).filter((col) => col.trim() !== "");
|
||||
|
||||
const key = columns[indexKey].trim();
|
||||
const synonyms = columns[indexSynonyms].trim();
|
||||
let synonyms = columns[indexSynonyms].trim();
|
||||
if (standardize) synonyms = synonyms.standardize();
|
||||
if (key !== "" && synonyms !== "") {
|
||||
if (thesaurus[key] === undefined) {
|
||||
thesaurus[key] = new Set();
|
||||
thesaurus[key].add(synonyms);
|
||||
} else {
|
||||
thesaurus[key].add(synonyms);
|
||||
}
|
||||
if (thesaurus[key] === undefined) thesaurus[key] = new Set();
|
||||
thesaurus[key].add(synonyms);
|
||||
}
|
||||
}
|
||||
return thesaurus;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
import type { Thesaurus } from "../interfaces";
|
||||
|
||||
export function getTags(content: string, thesaurus: Thesaurus) {
|
||||
export function getTags(
|
||||
content: string,
|
||||
thesaurus: Thesaurus,
|
||||
standardize?: boolean
|
||||
): string[] {
|
||||
const tagsToAdd: string[] = [];
|
||||
|
||||
if (standardize) content = content.standardize();
|
||||
for (const tags of Object.keys(thesaurus)) {
|
||||
const synonyms = thesaurus[tags];
|
||||
const regex = new RegExp(`\\b(${[...synonyms].join("|")})\\b`, "gi");
|
||||
|
|
|
|||
13
tests/__constants__.ts
Normal file
13
tests/__constants__.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import type { ColumnName, Translation } from "../src/interfaces";
|
||||
|
||||
export const mockTranslation = ((key: string, options?: any) => {
|
||||
const translations: { [key: string]: string } = {
|
||||
"error.csv.columns": "Invalid column names",
|
||||
"error.csv.separator": `Invalid separator: ${options?.sep}`,
|
||||
"error.csv.header": `Invalid header length: ${options?.len}`,
|
||||
"error.csv.malformed": `Your CSV is malformed: there is no column.`,
|
||||
};
|
||||
return translations[key] || key;
|
||||
}) as Translation;
|
||||
|
||||
export const columnNames: ColumnName = { term: "Term", synonyms: "Synonyms" };
|
||||
|
|
@ -1,20 +1,12 @@
|
|||
import { describe, expect, it } from "bun:test";
|
||||
import type { ColumnName, Separator, Translation } from "../src/interfaces";
|
||||
import { getThesaurus } from "../src/utils";
|
||||
import type { Separator } from "../src/interfaces";
|
||||
import { getHeader, getThesaurus } from "../src/utils";
|
||||
import "uniformize";
|
||||
import { getColumn } from "../src/utils/parse_csv";
|
||||
import { columnNames, mockTranslation } from "./__constants__";
|
||||
|
||||
const mockTranslation = ((key: string, options?: any) => {
|
||||
const translations: { [key: string]: string } = {
|
||||
"error.csv.columns": "Invalid column names",
|
||||
"error.csv.separator": `Invalid separator: ${options?.sep}`,
|
||||
"error.csv.header": `Invalid header length: ${options?.len}`,
|
||||
"error.csv.malformed": `Malformed line with length: ${options?.len}`,
|
||||
};
|
||||
return translations[key] || key;
|
||||
}) as Translation;
|
||||
const columnNames: ColumnName = { term: "Term", synonyms: "Synonyms" };
|
||||
describe("getThesaurus", () => {
|
||||
it("should parse a valid CSV content", () => {
|
||||
it("passing", () => {
|
||||
const csvContent = "Term,Synonyms\nword1,synonym1\nword2,synonym2";
|
||||
const separator: Separator = ",";
|
||||
const thesaurus = getThesaurus(csvContent, separator, mockTranslation, columnNames);
|
||||
|
|
@ -25,7 +17,7 @@ describe("getThesaurus", () => {
|
|||
});
|
||||
});
|
||||
describe("error handling", () => {
|
||||
it("should throw an error for invalid column names", () => {
|
||||
it("invalid column names", () => {
|
||||
const csvContent = "Invalid,Header\nword1,synonym1";
|
||||
const separator: Separator = ",";
|
||||
expect(() =>
|
||||
|
|
@ -33,7 +25,7 @@ describe("error handling", () => {
|
|||
).toThrow("Invalid column names");
|
||||
});
|
||||
|
||||
it("should throw an error for invalid separator", () => {
|
||||
it("invalid separator", () => {
|
||||
const csvContent = "Term;Synonyms\nword1;synonym1";
|
||||
const separator: Separator = ",";
|
||||
expect(() =>
|
||||
|
|
@ -41,24 +33,16 @@ describe("error handling", () => {
|
|||
).toThrow("Invalid separator: ;");
|
||||
});
|
||||
|
||||
it("should throw an error for invalid header length", () => {
|
||||
const csvContent = "Term,Synonyms,Extra\nword1,synonym1";
|
||||
it("malformed line", () => {
|
||||
const csvContent = "xxx,xxxx\nword1,synonym1";
|
||||
const separator: Separator = ",";
|
||||
expect(() =>
|
||||
getThesaurus(csvContent, separator, mockTranslation, columnNames)
|
||||
).toThrow("Invalid header length: 3");
|
||||
});
|
||||
|
||||
it("should throw an error for malformed line", () => {
|
||||
const csvContent = "Term,Synonyms\nword1,synonym1\nword2";
|
||||
const separator: Separator = ",";
|
||||
expect(() =>
|
||||
getThesaurus(csvContent, separator, mockTranslation, columnNames)
|
||||
).toThrow("Malformed line with length: 1");
|
||||
).toThrow(mockTranslation("error.csv.columns"));
|
||||
});
|
||||
});
|
||||
describe("duplicate terms and synonyms", () => {
|
||||
it("should not have duplicate terms in the result", () => {
|
||||
it("terms", () => {
|
||||
const csvContent = "Term,Synonyms\nword1,synonym1\nword1,synonym2";
|
||||
const separator: Separator = ",";
|
||||
const thesaurus = getThesaurus(csvContent, separator, mockTranslation, columnNames);
|
||||
|
|
@ -66,7 +50,7 @@ describe("duplicate terms and synonyms", () => {
|
|||
expect(thesaurus["word1"]).toEqual(new Set(["synonym1", "synonym2"]));
|
||||
});
|
||||
|
||||
it("should not have duplicate synonyms in the result", () => {
|
||||
it("synonyms", () => {
|
||||
const csvContent = "Term,Synonyms\nword1,synonym1\nword1,synonym1";
|
||||
const separator: Separator = ",";
|
||||
const thesaurus = getThesaurus(csvContent, separator, mockTranslation, columnNames);
|
||||
|
|
@ -74,7 +58,7 @@ describe("duplicate terms and synonyms", () => {
|
|||
expect(thesaurus["word1"]).toEqual(new Set(["synonym1"]));
|
||||
});
|
||||
|
||||
it("should not have duplicate terms and synonyms in the result", () => {
|
||||
it("terms and synonyms", () => {
|
||||
const csvContent = "Term,Synonyms\nword1,synonym1\nword2,synonym1";
|
||||
const separator: Separator = ",";
|
||||
const thesaurus = getThesaurus(csvContent, separator, mockTranslation, columnNames);
|
||||
|
|
@ -83,3 +67,83 @@ describe("duplicate terms and synonyms", () => {
|
|||
expect(thesaurus["word2"]).toEqual(new Set(["synonym1"]));
|
||||
});
|
||||
});
|
||||
|
||||
it("with accents", () => {
|
||||
const csvContent = "Term,Synonyms\ncafé,café\nrôle,rôle";
|
||||
const expected = {
|
||||
café: new Set(["cafe"]),
|
||||
rôle: new Set(["role"]),
|
||||
};
|
||||
const separator: Separator = ",";
|
||||
const thesaurus = getThesaurus(
|
||||
csvContent,
|
||||
separator,
|
||||
mockTranslation,
|
||||
columnNames,
|
||||
true
|
||||
);
|
||||
expect(thesaurus).toEqual(expected);
|
||||
});
|
||||
|
||||
describe("more than two column", () => {
|
||||
const csvContent = "Term,Synonyms,Extra\nword1,synonym1, notes";
|
||||
const separator: Separator = ",";
|
||||
it("pass with a simple csv", () => {
|
||||
const expectedColumnIndex = {
|
||||
indexKey: 0,
|
||||
indexSynonyms: 1,
|
||||
};
|
||||
const header = getHeader(csvContent.split("\n"), separator);
|
||||
const column = getColumn(header, columnNames, mockTranslation);
|
||||
expect(column).toEqual(expectedColumnIndex);
|
||||
});
|
||||
it("should error for invalid column names", () => {
|
||||
const columnNames = { term: "Terms", synonyms: "Synonyme" };
|
||||
const header = getHeader(csvContent.split("\n"), separator);
|
||||
expect(() => getColumn(header, columnNames, mockTranslation)).toThrow(
|
||||
"Invalid column names"
|
||||
);
|
||||
});
|
||||
it("thesaurus pass", () => {
|
||||
const thesaurus = getThesaurus(csvContent, separator, mockTranslation, columnNames);
|
||||
expect(thesaurus).toEqual({
|
||||
word1: new Set(["synonym1"]),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Unordered columns", () => {
|
||||
const csvContent = "Term, Definition, Synonyms\nword1, definition1, synonym1";
|
||||
const separator: Separator = ",";
|
||||
describe("should pass", () => {
|
||||
const expectedColumnIndex = {
|
||||
indexKey: 0,
|
||||
indexSynonyms: 2,
|
||||
};
|
||||
const header = getHeader(csvContent.split("\n"), separator);
|
||||
const column = getColumn(header, columnNames, mockTranslation);
|
||||
expect(column).toEqual(expectedColumnIndex);
|
||||
});
|
||||
it("shouldn't pass : not recognize column", () => {
|
||||
const invalidColumnNames = { term: "Word", synonyms: "Synonyms" };
|
||||
expect(() =>
|
||||
getThesaurus(csvContent, separator, mockTranslation, invalidColumnNames)
|
||||
).toThrow("Invalid column names");
|
||||
});
|
||||
it("getThesaurus", () => {
|
||||
const thesaurus = getThesaurus(csvContent, separator, mockTranslation, columnNames);
|
||||
expect(thesaurus).toEqual({
|
||||
word1: new Set(["synonym1"]),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("not enough columns", () => {
|
||||
const csvContent = "Term\nword1,word2";
|
||||
const separator: Separator = ",";
|
||||
it("should error", () => {
|
||||
expect(() =>
|
||||
getThesaurus(csvContent, separator, mockTranslation, columnNames)
|
||||
).toThrow(mockTranslation("error.csv.malformed"));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import type { Thesaurus } from "../src/interfaces";
|
|||
import { getTags } from "../src/utils";
|
||||
|
||||
describe("should return tags", () => {
|
||||
it("should return tags if synonyms are found in the content", () => {
|
||||
it("passing test", () => {
|
||||
const content = "This content contains synonym1 and synonym4.";
|
||||
const thesaurus: Thesaurus = {
|
||||
tag1: new Set(["synonym1", "synonym2"]),
|
||||
|
|
@ -14,7 +14,7 @@ describe("should return tags", () => {
|
|||
expect(result).toEqual(["tag1", "tag2"]);
|
||||
});
|
||||
|
||||
it("should handle case insensitive matches", () => {
|
||||
it("case insensitive", () => {
|
||||
const content = "This content contains Synonym1 and SYNONYM4.";
|
||||
const thesaurus: Thesaurus = {
|
||||
tag1: new Set(["synonym1", "synonym2"]),
|
||||
|
|
@ -24,7 +24,7 @@ describe("should return tags", () => {
|
|||
const result = getTags(content, thesaurus);
|
||||
expect(result).toEqual(["tag1", "tag2"]);
|
||||
});
|
||||
it("should return the two tags related to the synonyms", () => {
|
||||
it("synonym on the same tags", () => {
|
||||
const content = "This content contains synonym4.";
|
||||
const thesaurus: Thesaurus = {
|
||||
tag1: new Set(["synonym1", "synonym4"]),
|
||||
|
|
@ -35,13 +35,12 @@ describe("should return tags", () => {
|
|||
expect(result).toEqual(["tag1", "tag2"]);
|
||||
});
|
||||
|
||||
it("should return the tags if the synonyms is equal to the tag", () => {
|
||||
it("synonym = tag", () => {
|
||||
const content = "This content contains tag1.";
|
||||
const thesaurus: Thesaurus = {
|
||||
tag1: new Set(["tag1", "synonym1"]),
|
||||
tag2: new Set(["synonym3", "synonym4"]),
|
||||
};
|
||||
|
||||
const result = getTags(content, thesaurus);
|
||||
expect(result).toEqual(["tag1"]);
|
||||
});
|
||||
|
|
@ -78,3 +77,20 @@ describe("should not return tags", () => {
|
|||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Find without accents", () => {
|
||||
const thesaurus: Thesaurus = {
|
||||
cafe: new Set(["cafe", "chocolate"]),
|
||||
role: new Set(["role", "roliste"]),
|
||||
};
|
||||
it("should find tag with accents", () => {
|
||||
const content = "This content contains café.";
|
||||
const result = getTags(content, thesaurus, true);
|
||||
expect(result).toEqual(["cafe"]);
|
||||
});
|
||||
it("should find tag without accents", () => {
|
||||
const content = "This content contains cafe.";
|
||||
const result = getTags(content, thesaurus, true);
|
||||
expect(result).toEqual(["cafe"]);
|
||||
});
|
||||
});
|
||||
94
tests/parse_markdown.test.ts
Normal file
94
tests/parse_markdown.test.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import { describe, expect, it } from "bun:test";
|
||||
import type { Separator } from "../src/interfaces";
|
||||
import { getHeader, getThesaurus } from "../src/utils";
|
||||
import { columnNames, mockTranslation } from "./__constants__";
|
||||
import "uniformize";
|
||||
import { getColumn } from "../src/utils/parse_csv";
|
||||
it("validate header", () => {
|
||||
const csvContent =
|
||||
"| Term | Synonyms |\n| --- | --- |\n| word1 | synonym1\nword2 | synonym2 |";
|
||||
const separator: Separator = "|";
|
||||
const fileContent = csvContent.split("\n");
|
||||
const header = getHeader(fileContent, separator);
|
||||
expect(header).toEqual(["Term", "Synonyms"]);
|
||||
});
|
||||
it("passing", () => {
|
||||
const csvContent =
|
||||
"| Term | Synonyms |\n" +
|
||||
"| ----- | -------- |\n" +
|
||||
"| word1 | synonym1 |\n" +
|
||||
"| word2 | synonym2 |\n";
|
||||
const separator: Separator = "md";
|
||||
const thesaurus = getThesaurus(csvContent, separator, mockTranslation, columnNames);
|
||||
expect(thesaurus).toEqual({
|
||||
word1: new Set(["synonym1"]),
|
||||
word2: new Set(["synonym2"]),
|
||||
});
|
||||
});
|
||||
it("with accents", () => {
|
||||
const csvContent =
|
||||
"| Term | Synonyms |\n" +
|
||||
"| ----- | -------- |\n" +
|
||||
"| café | café |\n" +
|
||||
"| rôle | rôle |\n";
|
||||
const expected = {
|
||||
café: new Set(["cafe"]),
|
||||
rôle: new Set(["role"]),
|
||||
};
|
||||
const separator: Separator = "md";
|
||||
const thesaurus = getThesaurus(
|
||||
csvContent,
|
||||
separator,
|
||||
mockTranslation,
|
||||
columnNames,
|
||||
true
|
||||
);
|
||||
expect(thesaurus).toEqual(expected);
|
||||
});
|
||||
describe("More than two columns", () => {
|
||||
const csvContent =
|
||||
"| Term | Synonyms | Definition |\n" +
|
||||
"| ----- | -------- | ---------- |\n" +
|
||||
"| word1 | synonym1 | definition1 |\n" +
|
||||
"| word2 | synonym2 | definition2 |\n";
|
||||
const expected = {
|
||||
word1: new Set(["synonym1"]),
|
||||
word2: new Set(["synonym2"]),
|
||||
};
|
||||
it("get thesaurus", () => {
|
||||
const separator: Separator = "md";
|
||||
const thesaurus = getThesaurus(csvContent, separator, mockTranslation, columnNames);
|
||||
expect(thesaurus).toEqual(expected);
|
||||
});
|
||||
it("verify header", () => {
|
||||
const separator: Separator = "|";
|
||||
const fileContent = csvContent.split("\n");
|
||||
const header = getHeader(fileContent, separator);
|
||||
const expectedColumnIndex = {
|
||||
indexKey: 0,
|
||||
indexSynonyms: 1,
|
||||
};
|
||||
const column = getColumn(header, columnNames, mockTranslation);
|
||||
expect(column).toEqual(expectedColumnIndex);
|
||||
});
|
||||
it("shouldn't pass : not recognize column", () => {
|
||||
const separator: Separator = "md";
|
||||
const invalidColumnNames = { term: "Word", synonyms: "Synonyms" };
|
||||
expect(() =>
|
||||
getThesaurus(csvContent, separator, mockTranslation, invalidColumnNames)
|
||||
).toThrow(mockTranslation("error.csv.columns"));
|
||||
});
|
||||
it("Not ordered columns", () => {
|
||||
const csvContent =
|
||||
"| Term | Definition | Synonyms |\n" +
|
||||
"| ----- | -------- | ---------- |\n" +
|
||||
"| word1 | Def2 | synonym1 |\n" +
|
||||
"| word2 | Def1 | synonym2 |\n";
|
||||
const separator: Separator = "md";
|
||||
const thesaurus = getThesaurus(csvContent, separator, mockTranslation, columnNames);
|
||||
expect(thesaurus).toEqual({
|
||||
word1: new Set(["synonym1"]),
|
||||
word2: new Set(["synonym2"]),
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue