Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F3756283
D2582.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
15 KB
Referenced Files
None
Subscribers
None
D2582.diff
View Options
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("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
Details
Attached
Mime Type
text/plain
Expires
Tue, Nov 19, 22:23 (21 h, 39 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2253079
Default Alt Text
D2582.diff (15 KB)
Attached To
Mode
D2582: Merge all JetBrains IDEs application-level dictionaries
Attached
Detach File
Event Timeline
Log In to Comment