Page MenuHomeDevCentral

D2582.id6520.diff
No OneTemporary

D2582.id6520.diff

diff --git a/.arcconfig b/.arcconfig
new file mode 100644
--- /dev/null
+++ b/.arcconfig
@@ -0,0 +1,4 @@
+{
+ "phabricator.uri": "https://devcentral.nasqueron.org/",
+ "repository.callsign": "MD"
+}
diff --git a/.arclint b/.arclint
new file mode 100644
--- /dev/null
+++ b/.arclint
@@ -0,0 +1,29 @@
+{
+ "linters": {
+ "chmod": {
+ "type": "chmod"
+ },
+ "filename": {
+ "type": "filename"
+ },
+ "json": {
+ "type": "json",
+ "include": [
+ "(^\\.arcconfig$)",
+ "(^\\.arclint$)",
+ "(\\.json$)"
+ ]
+ },
+ "python": {
+ "type": "flake8",
+ "severity": {
+ "E203": "disabled",
+ "F821": "advice"
+ },
+ "include": [
+ "(^bin/merge-dictionaries$)",
+ "(\\.py$)"
+ ]
+ }
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+# Python package build
+dist/
+*.egg-info/
+
+# Python
+__pycache__/
+*.pyc
+*.pyo
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+Copyright 2022 Sébastien Santoro aka Dereckson, from Nasqueron.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile b/Makefile
new file mode 100644
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,43 @@
+# -------------------------------------------------------------
+# Merge dictionaries
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+RMDIR=rm -rf
+PYTHON=python
+DISCOVER_TESTS=$(PYTHON) -m unittest discover
+REFORMAT=black
+
+# -------------------------------------------------------------
+# Main targets
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+all:
+
+package: dist
+
+clean: clean-package
+
+test:
+ ${DISCOVER_TESTS} tests/
+
+# -------------------------------------------------------------
+# Development helpers
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+reformat:
+ find bin -type f -name '*.py' | xargs ${REFORMAT}
+ find src -type f -name '*.py' | xargs ${REFORMAT}
+ find tests -type f -name '*.py' | xargs ${REFORMAT}
+
+# -------------------------------------------------------------
+# Packaging targets
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+dist:
+ ${PYTHON} -m build
+
+clean-package:
+ ${RMDIR} dist src/merge_dictionaries.egg-info
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,82 @@
+# Merge dictionaries
+
+## Root problem
+
+You uses everal IDEs and each maintain its own spelling dictionary.
+
+You want to merge them so words from PyCharm are available in PhpStorm too.
+
+## Usage
+
+### Merge all dictionaries
+
+To discover dictionaries in your computer, extract words and merge them:
+
+```shell
+$ merge-dictionaries --merge
+```
+
+This is a potentially destructive operation:
+your dictionary files will be overwritten.
+
+### Extract dictionaries words
+
+To print all the words:
+
+```shell
+$ merge-dictionaries --extract
+```
+
+This is a safe operation.
+
+### Build an Hunspell-compatible dictionary
+
+To create a personal dictionary file for your Hunspell dictionary:
+
+```shell
+$ merge-dictionaries --extract > perso.dic
+```
+
+This is a safe read-only operation,
+as long as perso.dic doesn't already exist in your current folder.
+
+### Build a dictionary in a IDE specific format
+
+You can specify `--format=<format>` as argument to the extract task:
+
+```shell
+$ merge-dictionaries --extract --format=JetBrains
+```
+
+It will output a dictionary file you can use in any IDE compatible with that format.
+
+This is a safe read-only operation.
+
+## IDE support
+
+Currently, the following IDEs are supported
+
+* All JetBrains IDEs: application-level dictionary
+
+## Extend the code
+### How to add an IDE?
+
+To add an IDE, you need to provide the following methods:
+
+* sources
+ * a list of paths candidates for the IDE dictionary
+ * a method extracting words from the dictionary
+* output
+ * a method to dump the extracted words in the IDE format
+* write
+ * a method to save the files, normally you can call the ones created
+
+### How can I contribute?
+
+You can commit your changes to the upstream by following instructions at https://agora.nasqueron.org/How_to_contribute_code
+
+The canonical repository is https://devcentral.nasqueron.org/source/merge-dictionaries.git
+
+## License
+
+BSD-2-Clause, see [LICENSE](LICENSE) file.
diff --git a/bin/merge-dictionaries b/bin/merge-dictionaries
new file mode 100755
--- /dev/null
+++ b/bin/merge-dictionaries
@@ -0,0 +1,17 @@
+#!/usr/bin/env python3
+
+# -------------------------------------------------------------
+# Merge dictionaries :: Application
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# Description: Merge dictionaries from various sources,
+# mainly IDEs, and allow to propagate them.
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+
+from mergedictionaries import app
+
+
+if __name__ == "__main__":
+ app.run()
diff --git a/dev-requirements.txt b/dev-requirements.txt
new file mode 100644
--- /dev/null
+++ b/dev-requirements.txt
@@ -0,0 +1 @@
+unittest-data-provider>=1.0.1,<2.0
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,14 @@
+# -------------------------------------------------------------
+# Resolve hash
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+[build-system]
+requires = [
+ "setuptools>=42",
+ "wheel"
+]
+
+build-backend = "setuptools.build_meta"
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,31 @@
+[metadata]
+name = merge-dictionaries
+version = 0.1.0
+author = Sébastien Santoro
+author_email = dereckson@espace-win.org
+description = Merge dictionaries
+long_description = file: README.md
+long_description_content_type = text/markdown
+license = BSD-2-Clause
+license_files = LICENSE
+url = https://devcentral.nasqueron.org/source/merge-dictionaries/
+project_urls =
+ Bug Tracker = https://devcentral.nasqueron.org/tag/development_tools/
+classifiers =
+ Programming Language :: Python :: 3
+ License :: OSI Approved :: BSD License
+ Operating System :: OS Independent
+ Environment :: Console
+ Intended Audience :: Developers
+ Topic :: Software Development :: Build Tools
+
+[options]
+package_dir =
+ = src
+packages = find:
+scripts =
+ bin/merge-dictionaries
+python_requires = >=3.6
+
+[options.packages.find]
+where = src
diff --git a/src/mergedictionaries/__init__.py b/src/mergedictionaries/__init__.py
new file mode 100644
diff --git a/src/mergedictionaries/app/__init__.py b/src/mergedictionaries/app/__init__.py
new file mode 100644
--- /dev/null
+++ b/src/mergedictionaries/app/__init__.py
@@ -0,0 +1 @@
+from .app import run
diff --git a/src/mergedictionaries/app/app.py b/src/mergedictionaries/app/app.py
new file mode 100644
--- /dev/null
+++ b/src/mergedictionaries/app/app.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python3
+
+# -------------------------------------------------------------
+# Merge dictionaries
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# Description: Merge dictionaries from various sources,
+# mainly IDEs, and allow to propagate them.
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+
+import argparse
+import sys
+
+from mergedictionaries.sources import jetbrains as jetbrains_source
+from mergedictionaries.output import jetbrains as jetbrains_output
+from mergedictionaries.write import jetbrains as jetbrains_write
+
+
+# -------------------------------------------------------------
+# Extract words
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+def get_words_sources():
+ return [
+ jetbrains_source.extract_words_from_all_dictionaries,
+ ]
+
+
+def get_dictionary_formatters():
+ return {
+ "JetBrains": jetbrains_output.dump,
+ }
+
+
+def extract_all_words():
+ return sorted([words for method in get_words_sources() for words in method()])
+
+
+def run_extract_all_words(words_format):
+ words = extract_all_words()
+
+ # Trivial case
+ if words_format == "text":
+ for word in words:
+ print(word)
+ sys.exit(0)
+
+ # We need a specific formatter
+ formatters = get_dictionary_formatters()
+ if words_format not in formatters:
+ print(f"Unknown format: {words_format}", file=sys.stderr)
+ sys.exit(2)
+
+ print(formatters[words_format](words))
+ sys.exit(0)
+
+
+# -------------------------------------------------------------
+# Merge all dictionaries
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+def get_dictionary_writers():
+ return [
+ jetbrains_write.write,
+ ]
+
+
+def run_merge():
+ words = extract_all_words()
+
+ for method in get_dictionary_writers():
+ method(words)
+
+
+# -------------------------------------------------------------
+# Application entry point
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+def parse_arguments():
+ parser = argparse.ArgumentParser(description="Merge dictionaries.")
+
+ parser.add_argument(
+ "--extract",
+ action="store_const",
+ dest="task",
+ const="extract",
+ help="Extract all words from found dictionaries",
+ )
+ parser.add_argument(
+ "--format", action="store", help="Specifies the output format", default="text"
+ )
+
+ parser.add_argument(
+ "--merge",
+ action="store_const",
+ dest="task",
+ const="merge",
+ help="Merge all found dictionaries",
+ )
+
+ return parser.parse_args()
+
+
+def run():
+ args = parse_arguments()
+
+ if args.task is None:
+ print(f"No task has been specified.", file=sys.stderr)
+ sys.exit(1)
+
+ if args.task == "extract":
+ run_extract_all_words(args.format)
+ elif args.task == "merge":
+ run_merge()
diff --git a/src/mergedictionaries/output/__init__.py b/src/mergedictionaries/output/__init__.py
new file mode 100644
diff --git a/src/mergedictionaries/output/jetbrains.py b/src/mergedictionaries/output/jetbrains.py
new file mode 100644
--- /dev/null
+++ b/src/mergedictionaries/output/jetbrains.py
@@ -0,0 +1,45 @@
+# -------------------------------------------------------------
+# Merge dictionaries :: Output :: JetBrains XML format
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# Description: Find application-level dictionaries
+# from JetBrains IDEs
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+
+from io import StringIO
+from sys import version_info
+from xml.etree import ElementTree
+
+
+def get_xml_tree(words):
+ root = ElementTree.Element("application")
+ component = ElementTree.SubElement(
+ root,
+ "component",
+ attrib={
+ "name": "CachedDictionaryState",
+ },
+ )
+
+ words_element = ElementTree.SubElement(component, "words")
+ for word in words:
+ word_element = ElementTree.SubElement(words_element, "w")
+ word_element.text = word
+
+ return ElementTree.ElementTree(root)
+
+
+def dump(words):
+ root = get_xml_tree(words)
+
+ if version_info >= (3, 9):
+ ElementTree.indent(root)
+
+ output = StringIO()
+ root.write(output, encoding="unicode")
+ contents = output.getvalue()
+ output.close()
+
+ return contents
diff --git a/src/mergedictionaries/sources/__init__.py b/src/mergedictionaries/sources/__init__.py
new file mode 100644
diff --git a/src/mergedictionaries/sources/jetbrains.py b/src/mergedictionaries/sources/jetbrains.py
new file mode 100644
--- /dev/null
+++ b/src/mergedictionaries/sources/jetbrains.py
@@ -0,0 +1,45 @@
+# -------------------------------------------------------------
+# Merge dictionaries :: Sources :: JetBrains IDEs
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# Description: Find application-level dictionaries
+# from JetBrains IDEs
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+
+import os
+from xml.etree import ElementTree
+
+
+def get_configuration_path():
+ return os.environ["HOME"] + "/.config/JetBrains"
+
+
+def find_application_level_dictionaries():
+ # We're looking for paths like:
+ # ~/.config/JetBrains/PyCharm2021.3/options/cachedDictionary.xml
+ base_path = get_configuration_path()
+
+ return [
+ candidate
+ for candidate in [
+ os.path.join(base_path, entry.name, "options", "cachedDictionary.xml")
+ for entry in os.scandir(base_path)
+ if entry.is_dir()
+ ]
+ if os.path.exists(candidate)
+ ]
+
+
+def extract_words(dictionary_path):
+ tree = ElementTree.parse(dictionary_path)
+ return [word.text for word in tree.getroot()[0][0]]
+
+
+def extract_words_from_all_dictionaries():
+ return {
+ word
+ for dictionary_path in find_application_level_dictionaries()
+ for word in extract_words(dictionary_path)
+ }
diff --git a/src/mergedictionaries/write/__init__.py b/src/mergedictionaries/write/__init__.py
new file mode 100644
diff --git a/src/mergedictionaries/write/jetbrains.py b/src/mergedictionaries/write/jetbrains.py
new file mode 100644
--- /dev/null
+++ b/src/mergedictionaries/write/jetbrains.py
@@ -0,0 +1,21 @@
+# -------------------------------------------------------------
+# Merge dictionaries :: Publishers :: JetBrains IDEs
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# Description: Find application-level dictionaries
+# from JetBrains IDEs
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+
+from mergedictionaries.sources import jetbrains as jetbrains_source
+from mergedictionaries.output import jetbrains as jetbrains_output
+
+
+def write(words):
+ contents = jetbrains_output.dump(words)
+
+ for file_path in jetbrains_source.find_application_level_dictionaries():
+ with open(file_path, "w") as fd:
+ fd.write(contents)
+ fd.write("\n")

File Metadata

Mime Type
text/plain
Expires
Wed, Nov 20, 00:24 (21 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2253131
Default Alt Text
D2582.id6520.diff (15 KB)

Event Timeline