├── .github └── workflows │ └── python-publish.yml ├── .gitignore ├── DOCUMENTATION.md ├── LICENSE ├── README.md ├── examples ├── code_card.png ├── codeblock_example.py └── codeeditor_example.py ├── setup.py ├── tests └── test_codeblock.py └── tkcode ├── __init__.py ├── codeblock.py ├── codebox.py ├── codeeditor.py └── schemes ├── azure.json ├── dracula.json ├── mariana.json ├── monokai-plus-plus.json └── monokai.json /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Upload tkcode to Pypi 5 | 6 | on: 7 | release: 8 | types: [published] 9 | 10 | jobs: 11 | deploy: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Python 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: 3.9 21 | - name: Install the dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install setuptools wheel twine pygments 25 | - name: Build and publish to Pypi 26 | env: 27 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 28 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 29 | run: | 30 | python setup.py sdist bdist_wheel 31 | twine upload dist/* 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /DOCUMENTATION.md: -------------------------------------------------------------------------------- 1 | ## Initialization arguments 2 | 3 | Argument | Description | Type | Default (on X11) 4 | -|-|-|- 5 | `autofocus` | If True the widget will automatically get focus on initialization. | bool | False 6 | `autoseparators` | If this option is True and the `undo` option is set, the separators are automatically added to the undo stack after each insertion or deletion. | bool | True 7 | `bg` or `background` | The default background color of the text widget. The option might overwritten by the style configuration file. | str | #ffffff 8 | `bd` or `borderwidth` | The width of the border around the text widget. The option might overwritten by the style configuration file. | int | 2 9 | `blockcursor` | If True the insertion cursor should be a character-sized rectangle. In the `CodeEditor` widget you can change between block, and line cursor with the `Insert` key. | bool | False 10 | `cursor` | The cursor used inside the widget. See https://www.tcl.tk/man/tcl/TkCmd/cursors.html for a full list of cursors. | str | xterm 11 | `endline` | Specifies an integer line index representing the line of the underlying textual data store that should be just after the last line contained in the widget. This allows a text widget to reflect only a portion of a larger piece of text. If instead of an integer an empty string is given, it will configure the widget to end at the very last line in the textual data store. | int \| "" | "" 12 | `exportselection` | Normally, text selected within a text widget is exported to be the selection in the window manager. Set exportselection=0 if you don't want that behavior. | int | 1 13 | `font` | The default font for text inserted into the widget. | tuple | ("monospace", 10) 14 | `fg` or `foreground` | The color used for text (and bitmaps) within the widget. The option might overwritten by the style configuration file. | str | #000000 15 | `height` | The height of the widget in lines (not pixels!), measured according to the current font size. | int | 24 16 | `highlightbackground` | The color of the focus highlight when the text widget does not have focus. The option might overwritten by the style configuration file. | str | #d9d9d9 17 | `highlightcolor` | The color of the focus highlight when the text widget has the focus. The option might overwritten by the style configuration file. | str | #000000 18 | `highlighter` | | str | mariana 19 | `highlightthickness` | The thickness of the focus highlight. Default is 1. Set this option to 0 to suppress display of the focus highlight. The option might overwritten by the style configuration file. | int | 1 20 | `inactiveselectbackground` | Specifies the color to use for the selection when the window does not have the input focus. If empty, then no selection is shown when the window does not have the focus. The option might overwritten by the style configuration file. | str | #c3c3c3 21 | `insertbackground` | The color of the insertion cursor. The option might overwritten by the style configuration file. | str | #000000 22 | `insertborderwidth` | Size of the 3-D border around the insertion cursor. The option might overwritten by the style configuration file. | int | 0 23 | `insertofftime` | The number of milliseconds the insertion cursor is off during its blink cycle. Set this option to zero to turn off blinking. | int | 300 24 | `insertontime` | The number of milliseconds the insertion cursor is on during its blink cycle. | int | 600 25 | `insertwidth` | Width of the insertion cursor (its height is determined by the tallest item in its line). | int | 2 26 | `insertunfocussed` | Specifies how to display the insertion cursor when the widget does not have the focus. Valid values: `none` which means to not display the cursor, `hollow` which means to display a hollow box, or `solid` which means to display a solid box. The option might overwritten by the style configuration file. | str | none 27 | `language` | Syntax highlighting language. Supported languages: `Ada`, `Bash`, `Batch`, `Brainfuck`, `C`, `CMake`, `CoffeeScript`, `CSS`, `C#`, `C++`, `Dart`, `Delphi`, `Dockerfile`, `Fortran`, `Go`, `Groovy`, `Haskell`, `HTML`, `Java`, `JavaScript`, `JSON`, `Kotlin`, `Lisp`, `Lua`, `Matlab`, `Makefile`, `Assembly` (Nasm), `Objective-C`, `Perl`, `PHP`, `PowerShell`, `Python`, `R`, `Ruby`, `Swift`, `SQL`, `Tcl`, `TypeScript`, `Vim`, `YAML`, | str | python 28 | `maxundo` | This option sets the maximum number of operations retained on the undo stack. Set this option to -1 to specify an unlimited number of entries in the undo stack. | int | 0 29 | `padx` | The size of the internal padding added to the left and right of the text area. | int | 1 30 | `pady` | The size of the internal padding added above and below the text area. | int | 1 31 | `relief` | The 3-D appearance of the text widget. | str | sunken 32 | `selectbackground` | The background color to use displaying selected text. The option might overwritten by the style configuration file. | str | #c3c3c3 33 | `selectborderwidth` | The width of the border to use around selected text. The option might overwritten by the style configuration file. | int | 0 34 | `selectforeground` | The foreground color to use displaying selected text. The option might overwritten by the style configuration file. | str | #000000 35 | `setgrid` | Specifies a boolean value that determines whether this widget controls the resizing grid for its top-level window. This option is typically used in text widgets, where the information in the widget has a natural size (the size of a character) and it makes sense for the window's dimensions to be integral numbers of these units. These natural window sizes form a grid. If this option is set to `True` then the widget will communicate with the window manager so that when the user interactively resizes the top-level window that contains the widget, the dimensions of the window will be displayed to the user in grid units and the window size will be constrained to integral numbers of grid units. See https://www.tcl.tk/man/tcl8.5/TkLib/SetGrid.html for more info. | bool | False 36 | `spacing1` | Requests additional space above each text line in the widget, using any of the standard forms for screen distances. If a line wraps, this option only applies to the first line on the display. This option may be overridden with -spacing1 options in tags. | int | 0 37 | `spacing2` | For lines that wrap (so that they cover more than one line on the display) this option specifies additional space to provide between the display lines that represent a single line of text. The value may have any of the standard forms for screen distances. This option may be overridden with -spacing2 options in tags. | int | 0 38 | `spacing3` | Requests additional space below each text line in the widget, using any of the standard forms for screen distances. If a line wraps, this option only applies to the last line on the display. This option may be overridden with -spacing3 options in tags. | int | 0 39 | `startline` | Specifies an integer line index representing the first line of the underlying textual data store that should be contained in the widget. This allows a text widget to reflect only a portion of a larger piece of text. Instead of an integer, the empty string can be provided to this configuration option, which will configure the widget to start at the very first line in the textual data store. | int \| "" | "" 40 | ! `state` !| Determines whether the textbox is editable or not. Don't use it for CodeBlock. | str | normal (CodeEditor), disabled (CodeBlock) 41 | `tabs` | The size of a tab, note that unlike a plain textwidget, it should not be specified in screen distance, but in characters (`ch`) | str | 4ch 42 | `tabstyle` | Specifies how to interpret the relationship between tab stops on a line and tabs in the text of that line. The value must be `tabular` or `wordprocessor`. Note that tabs are interpreted as they are encountered in the text. If the tab style is tabular then the n'th tab character in the line's text will be associated with the n'th tab stop defined for that line. If the tab character's x coordinate falls to the right of the n'th tab stop, then a gap of a single space will be inserted as a fallback. If the tab style is `wordprocessor` then any tab character being laid out will use (and be defined by) the first tab stop to the right of the preceding characters already laid out on that line. | str | tabular 43 | `takefocus` | Determines whether the window accepts the focus during keyboard traversal (`Tab` or `Shift-Tab`). A value of `False` means that the window should be skipped entirely during keyboard traversal. `True` means that the window should receive the input focus as long as it is viewable (it and all of its ancestors are mapped). An empty string value for the option means that the traversal scripts make the decision about whether or not to focus on the window. | bool \| "" | "" 44 | `undo` | Specifies a boolean that says whether the undo mechanism is active or not. | bool | False 45 | `width` | The width of the widget in characters (not pixels!), measured according to the current font size. | int | 80 46 | ! `wrap` !| Specifies how to handle lines in the text that are too long to be displayed in a single line of the text's window. Valid values: `wrap` means that each line of text appears as exactly one line on the screen; extra characters that do not fit on the screen are not displayed. In `char` mode each line of text will be broken up into several screen lines if necessary to keep all the characters visible. In char mode a screen line break may occur after any character; in word mode a line break will only be made at word boundaries. In `CodeEditor` and `CodeBlock` this option is explicitly set to `none`. | str | none 47 | ! `xscrollcommand` !| Don't use it for CodeBlock. | callable | "" 48 | ! `yscrollcommand` !| Don't use it for CodeBlock. | callable | "" 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 rdbende 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `tkcode` 2 | 3 | ## DeprecationWarning: 4 | This module is obsolete. 5 | 6 | `tkcode` has not been maintained for a year as it was very buggy and I considered its code a piece of crap. 7 | So instead of fixing all the bugs and trying to carefully remove all the sh*t I messed up here, while keeping backwards compatibility, I started over with a new project. 8 | 9 | See [chlorophyll 🟢](https://github.com/rdbende/chlorophyll) for its successor. 10 | -------------------------------------------------------------------------------- /examples/code_card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rdbende/tkcode/fb6c754f816ef1b1c1d6266426465b701c9260ca/examples/code_card.png -------------------------------------------------------------------------------- /examples/codeblock_example.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author: rdbende 3 | License: GNU GPLv3 4 | Copyright: 2021 rdbende 5 | """ 6 | 7 | import tkinter as tk 8 | from tkinter import ttk 9 | 10 | from tkcode import CodeBlock 11 | 12 | root = tk.Tk() 13 | root.title("CodeBlock example") 14 | root.config(bg="#4e5a65") 15 | 16 | style = ttk.Style() 17 | 18 | card_im = tk.PhotoImage(file="code_card.png") 19 | 20 | style.layout("Card", [("Card.field", {"children": [("Card.padding", {"expand": 1})]})]) 21 | 22 | style.element_create( 23 | "Card.field", "image", card_im, border=20, padding=4, sticky="nsew" 24 | ) 25 | 26 | main_frame = ttk.Frame(root, padding=15, style="Card") 27 | main_frame.pack(fill="both", expand=True, padx=20, pady=20) 28 | 29 | code_block = CodeBlock(main_frame, width=42, height=9, highlighter="mariana") 30 | code_block.pack(expand=True, fill="both") 31 | 32 | code_block.content = """import tkinter as tk 33 | from tkcode import CodeEditor 34 | 35 | root = tk.Tk() 36 | 37 | text = CodeEditor(root, language="kotlin") 38 | text.pack(expand=True, fill="both") 39 | 40 | root.mainloop()""" 41 | 42 | root.update() 43 | root.minsize(root.winfo_width(), root.winfo_height()) 44 | root.mainloop() 45 | -------------------------------------------------------------------------------- /examples/codeeditor_example.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author: rdbende 3 | License: GNU GPLv3 4 | Copyright: 2021 rdbende 5 | """ 6 | 7 | import tkinter as tk 8 | from tkinter import ttk 9 | from tkcode import CodeEditor 10 | 11 | root = tk.Tk() 12 | root.title("CodeEditor example") 13 | root.option_add("*tearOff", 0) 14 | 15 | menubar = tk.Menu(root) 16 | 17 | file_menu = tk.Menu(menubar) 18 | file_menu.add_command(label="New") 19 | file_menu.add_command(label="Open") 20 | file_menu.add_command(label="Save") 21 | file_menu.add_command(label="Save as") 22 | file_menu.add_separator() 23 | file_menu.add_command(label="Exit") 24 | 25 | help_menu = tk.Menu(menubar) 26 | help_menu.add_command(label="Help") 27 | help_menu.add_command(label="About") 28 | 29 | menubar.add_cascade(menu=file_menu, label="File") 30 | menubar.add_cascade(menu=help_menu, label="Help") 31 | 32 | root.config(menu=menubar) 33 | 34 | notebook = ttk.Notebook(root) 35 | tab_1 = ttk.Frame(notebook) 36 | notebook.add(tab_1, text="hello.cpp") 37 | notebook.pack(fill="both", expand=True) 38 | 39 | code_editor = CodeEditor( 40 | tab_1, 41 | width=40, 42 | height=10, 43 | language="c++", 44 | highlighter="dracula", 45 | autofocus=True, 46 | blockcursor=True, 47 | insertofftime=0, 48 | padx=10, 49 | pady=10, 50 | ) 51 | 52 | code_editor.pack(fill="both", expand=True) 53 | 54 | code_editor.content = """#include 55 | using namespace std; 56 | int main() { 57 | \tcout << "Hello World!" << endl; 58 | \treturn 0; 59 | }""" 60 | 61 | 62 | root.update() 63 | root.minsize(root.winfo_width(), root.winfo_height()) 64 | root.mainloop() 65 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from setuptools import setup 4 | 5 | version = ( 6 | subprocess.check_output(["git", "describe", "--abbrev=0", "--tags"]) 7 | .strip() 8 | .decode() 9 | ) 10 | 11 | assert version[0] == "v" # Something went wrong 12 | version = version[1:] # remove the "v" 13 | 14 | with open("README.md", "r") as file: 15 | long_description = file.read() 16 | 17 | setup( 18 | name="tkcode", 19 | version=version, 20 | description="A code editor and code block widget for tkinter with syntax highlighting, and some other useful stuff", 21 | author="rdbende", 22 | author_email="rdbende@gmail.com", 23 | url="https://github.com/rdbende/tkcode", 24 | long_description=long_description, 25 | long_description_content_type="text/markdown", 26 | install_requires=["pygments"], 27 | python_requires=">=3.6", 28 | license="MIT license", 29 | classifiers=[ 30 | "Programming Language :: Python :: 3.6", 31 | "License :: OSI Approved :: MIT License", 32 | "Operating System :: OS Independent", 33 | ], 34 | packages=["tkcode"], 35 | package_data={"tkcode": ["schemes/*"]}, 36 | ) 37 | -------------------------------------------------------------------------------- /tests/test_codeblock.py: -------------------------------------------------------------------------------- 1 | """ 2 | A really simple test file for the most important features I need to test 3 | """ 4 | 5 | import unittest 6 | 7 | from tkcode import CodeBlock 8 | 9 | 10 | class TestCodeBlock(unittest.TestCase): 11 | def test_codeblock_init(self): 12 | widget = CodeBlock() 13 | widget.pack() 14 | 15 | def test_is_empty(self): 16 | widget = CodeBlock() 17 | self.assertTrue(widget.is_empty) 18 | self.assertEqual(widget.content, "\n") 19 | 20 | def test_content_change(self): 21 | widget = CodeBlock() 22 | widget.insert("end", "\n\n") 23 | self.assertEqual(widget.content, "\n\n\n") 24 | widget.content = "test" 25 | self.assertEqual(widget.content, "test\n") 26 | 27 | def test_line_numbers(self): 28 | widget = CodeBlock() 29 | widget.content = "\n\n" 30 | self.assertEqual(widget.number_of_lines, 3) 31 | 32 | def test_lang_config_methods(self): 33 | widget = CodeBlock(language="python") 34 | self.assertEqual(widget["language"], "python") 35 | widget["language"] = "kotlin" 36 | self.assertEqual(widget["language"], "kotlin") 37 | widget.config(language="cpp") 38 | self.assertEqual(widget.cget("language"), "cpp") 39 | widget.configure(language="brainfuck") 40 | self.assertEqual(widget.cget("language"), "brainfuck") 41 | 42 | 43 | if __name__ == "__main__": 44 | unittest.main() 45 | -------------------------------------------------------------------------------- /tkcode/__init__.py: -------------------------------------------------------------------------------- 1 | from .codeblock import CodeBlock 2 | from .codeeditor import CodeEditor 3 | -------------------------------------------------------------------------------- /tkcode/codeblock.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author: rdbende 3 | License: GNU GPLv3 4 | Copyright: 2021 rdbende 5 | """ 6 | 7 | import tkinter as tk 8 | 9 | from . import codebox 10 | 11 | 12 | class CodeBlock(codebox.BaseCodeBox): 13 | def __init__( 14 | self, 15 | master: tk.Misc, 16 | language="python", 17 | highlighter="mariana", 18 | autofocus=False, 19 | **kwargs 20 | ): 21 | kwargs.update({"state": "disabled"}) 22 | kwargs.pop("xscrollcommand", None) 23 | kwargs.pop("yscrollcommand", None) 24 | codebox.BaseCodeBox.__init__( 25 | self, master, language, highlighter, autofocus, **kwargs 26 | ) 27 | 28 | def disabler(func): 29 | def wrapper(self, *args, **kwargs): 30 | codebox.BaseCodeBox.config(self, state="normal") 31 | func(self, *args, **kwargs) 32 | codebox.BaseCodeBox.config(self, state="disabled") 33 | 34 | return wrapper 35 | 36 | @disabler 37 | def insert(self, *args, **kwargs): 38 | codebox.BaseCodeBox.insert(self, *args, **kwargs) 39 | 40 | @disabler 41 | def delete(self, *args, **kwargs): 42 | codebox.BaseCodeBox.delete(self, *args, **kwargs) 43 | 44 | @property 45 | def content(self): 46 | return self.get("1.0", "end") 47 | 48 | @content.setter 49 | @disabler 50 | def content(self, new_content): 51 | self.delete("1.0", "end") 52 | self.insert("end", new_content) 53 | -------------------------------------------------------------------------------- /tkcode/codebox.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author: rdbende 3 | License: GNU GPLv3 4 | Copyright: 2021 rdbende 5 | """ 6 | 7 | import json 8 | import os 9 | import tkinter as tk 10 | from tkinter import font as tkfont 11 | from tkinter import ttk 12 | from typing import Union 13 | 14 | import pygments 15 | from pygments.lexers import * 16 | 17 | 18 | class BaseCodeBox(tk.Text): 19 | languages = ( 20 | "Ada", 21 | "Bash", 22 | "Batch", 23 | "Brainfuck", 24 | "C", 25 | "CMake", 26 | "CoffeeScript", 27 | "CSS", 28 | "C#", 29 | "C++", 30 | "Dart", 31 | "Delphi", 32 | "Dockerfile", 33 | "Fortran", 34 | "Go", 35 | "Groovy", 36 | "Haskell", 37 | "HTML", 38 | "Java", 39 | "JavaScript", 40 | "JSON", 41 | "Kotlin", 42 | "Lisp", 43 | "Lua", 44 | "Matlab", 45 | "Makefile", 46 | "Nasm", # probably the most common 47 | "Objective-C", 48 | "Perl", 49 | "PHP", 50 | "PowerShell", 51 | "Python", 52 | "R", 53 | "Ruby", 54 | "Swift", 55 | "SQL", 56 | "Tcl", 57 | "TypeScript", 58 | "Vim", 59 | "YAML", 60 | ) 61 | 62 | def __init__( 63 | self, 64 | master: tk.Misc, 65 | language: str, 66 | highlighter: str, 67 | autofocus: bool, 68 | **kwargs, 69 | ) -> None: 70 | kwargs.update({"wrap": "none"}) 71 | 72 | tab_length = kwargs.pop("tabs", "4ch") 73 | if tab_length[-2:] == "ch": 74 | tab_length = int(tab_length[:-2]) 75 | else: 76 | raise ValueError( 77 | f"Invalid tab length '{tab_length}', please give it in characters, eg: '4ch'" 78 | ) 79 | 80 | self.frame = ttk.Frame(master) 81 | tk.Text.__init__(self, self.frame, **kwargs) 82 | 83 | tk.Text.grid(self, row=0, column=0, sticky="nsew") 84 | 85 | self._font = tkfont.Font(font=kwargs.pop("font", ("monospace", 10))) 86 | tab = self._font.measure(" " * tab_length) 87 | 88 | self.configure(font=self._font, tabs=tab) 89 | self.frame.grid_rowconfigure(0, weight=1) 90 | self.frame.grid_columnconfigure(0, weight=1) 91 | 92 | self._highlighter, self._language = None, None 93 | 94 | self.update_lexer(language) # Order is important! 95 | self.update_highlighter(highlighter) 96 | 97 | self._orig = self._w + "_orig" 98 | self.tk.call("rename", self._w, self._orig) 99 | self.tk.createcommand(self._w, self._proxy) 100 | 101 | if autofocus: 102 | self.focus() 103 | 104 | def _proxy(self, command, *args): 105 | """Thanks to Bryan Oakley on StackOverflow: https://stackoverflow.com/a/40618152/""" 106 | cmd = (self._orig, command) + args 107 | result = self.tk.call(cmd) 108 | 109 | # Generate a <> event if the widget content was modified 110 | 111 | if command in {"insert", "replace", "delete"}: 112 | self.event_generate("<>") 113 | 114 | return result # Returns what it would actually return 115 | 116 | def insert(self, index: str, content: str): 117 | # FIXME: imo this method is super hacky, there should be a better solution 118 | line_no = int( 119 | self.index(index).split(".")[ 120 | 0 121 | ] # Important! We don't want a text index "end.end" 122 | ) 123 | 124 | if len(content.splitlines()) > 1: 125 | for line in content.splitlines(): 126 | tk.Text.insert(self, f"{line_no}.end", line + "\n") 127 | self.mark_set("insert", f"{line_no}.end") 128 | self.highlight_line(line=line_no - 1) 129 | line_no += 1 130 | else: 131 | tk.Text.insert(self, index, content) 132 | self.highlight_line(line=line_no) 133 | self.see(f"{line_no}.0") 134 | return "break" 135 | 136 | def highlight_line(self, event: tk.Event = None, line: int = None) -> None: 137 | """Highlights the specified or the current line""" 138 | if line is None: 139 | line = int(self.index("insert").split(".")[0]) 140 | line_text = self.get(f"{line}.0", f"{line}.end") 141 | start = f"{line}.0" 142 | 143 | for tag in self.tag_names(index=None): 144 | if tag != "sel": 145 | # Don't clear selection when pressing Ctrl + a 146 | # because this method runs on every keypress 147 | self.tag_remove(tag, f"{line}.0", f"{line}.end") 148 | 149 | for token, content in pygments.lex(line_text, self._lexer()): 150 | end = f"{start.split('.')[0]}.{int(start.split('.')[1]) + len(content)}" 151 | self.tag_add(str(token), start, end) 152 | start = end 153 | 154 | def highlight_all(self, *_) -> None: 155 | """Loops through the entire content and highlights it""" 156 | for tag in self.tag_names(index=None): 157 | if tag != "sel": 158 | self.tag_remove(tag, "1.0", "end") 159 | 160 | for line in range(self.number_of_lines): 161 | self.highlight_line(line=line) 162 | 163 | self.event_generate("<>") 164 | 165 | def load_from_file(self, file_name: str): 166 | with open(file_name, "r") as file: 167 | self.delete("1.0", "end") 168 | self.insert("end", file.read()) 169 | self.event_generate("<>") 170 | 171 | def save_to_file( 172 | self, file_name: str, start: str = "1.0", end: str = "end - 1 char" 173 | ): 174 | with open(file_name, "w") as file: 175 | file.write(self.get(start, end)) 176 | self.event_generate("<>") 177 | 178 | @property 179 | def content(self) -> str: 180 | return self.get("1.0", "end") 181 | 182 | @content.setter 183 | def content(self, new_content: str) -> None: 184 | self.delete("1.0", "end") 185 | # FIXME: if index is `end` than both the code_editor 186 | # and the code_block's last line gets highlighted, but if it's 1.0, 187 | # than just the code_block, and code_editor's last line stays white 188 | self.insert("end", new_content) 189 | 190 | @property 191 | def language(self) -> Union[str, None]: 192 | return self._language 193 | 194 | @language.setter 195 | def language(self, language) -> None: 196 | self.update_lexer(language) 197 | 198 | @property 199 | def lexer(self) -> pygments.lexer.Lexer: 200 | return self._lexer 201 | 202 | @lexer.setter 203 | def lexer(self, lexer: str) -> None: 204 | self._lexer = lexer 205 | self.update_lexer("unknown") 206 | 207 | @property 208 | def font_family(self) -> str: 209 | return self._font.actual("family") 210 | 211 | @font_family.setter 212 | def font_family(self, family: str) -> None: 213 | self._font.config(family=family) 214 | 215 | @property 216 | def font_size(self) -> int: 217 | return self._font.actual("size") 218 | 219 | @font_size.setter 220 | def font_size(self, size: int) -> None: 221 | self._font.config(size=size) 222 | 223 | @property 224 | def font(self) -> tuple: 225 | return self._font.actual() 226 | 227 | @property 228 | def number_of_lines(self) -> int: 229 | return int(self.index("end - 1 char").split(".")[0]) 230 | 231 | @property 232 | def is_empty(self) -> bool: 233 | return self.get("1.0", "end") == "\n" 234 | 235 | def _generate_font_list(self, input_dict: dict) -> list: 236 | font_dict = {"-family": self.font_family, "-size": self.font_size} 237 | 238 | for style_key, style_value in input_dict.items(): 239 | if style_key == "family": 240 | font_dict["-family"] = style_value 241 | elif style_key == "size": 242 | font_dict["-size"] = style_value 243 | elif style_key == "bold": 244 | font_dict["-weight"] = "bold" if style_value else "normal" 245 | elif style_key == "italic": 246 | font_dict["-slant"] = "italic" if style_value else "roman" 247 | elif style_key == "underline": 248 | font_dict["-underline"] = style_value 249 | elif style_key == "strikethrough": 250 | font_dict["-overstrike"] = style_value 251 | 252 | font_list = [] 253 | for x, y in zip(font_dict.keys(), font_dict.values()): 254 | font_list.extend([x, y]) 255 | 256 | return font_list 257 | 258 | def update_highlighter(self, highlighter: str) -> None: 259 | """Sets or changes the highlighter configuration""" 260 | highlight_file = highlighter 261 | package_path = os.path.dirname(os.path.realpath(__file__)) 262 | 263 | if highlighter in [ 264 | x.split(".")[0] for x in os.listdir(os.path.join(package_path, "schemes")) 265 | ]: 266 | highlight_file = os.path.join( 267 | package_path, "schemes", highlighter + ".json" 268 | ) 269 | try: 270 | with open(highlight_file) as file: 271 | self.configuration = json.load(file) 272 | except FileNotFoundError: 273 | raise FileNotFoundError( 274 | f"Style configuration file not found: '{highlight_file}'" 275 | ) 276 | 277 | general_props = self.configuration.pop("general") 278 | selection_props = self.configuration.pop("selection") 279 | syntax_props = self.configuration.pop("syntax") 280 | 281 | self.config(**general_props) 282 | self.tag_configure("sel", **selection_props) 283 | 284 | for key, value in syntax_props.items(): 285 | if isinstance(value, str): 286 | self.tag_configure(key, foreground=value) 287 | else: 288 | if "font" in value: 289 | value["font"] = self._generate_font_list(value["font"]) 290 | self.tag_configure(key, **value) 291 | 292 | if self._highlighter: # Don't generate event on init 293 | self.event_generate("<>") 294 | 295 | self._highlighter = highlighter 296 | self.highlight_all() 297 | 298 | def update_lexer(self, language: Union[str, None] = None) -> None: 299 | """Sets or changes the Pygments lexer""" 300 | if not language: 301 | return 302 | 303 | self._set_lexer(language.lower()) 304 | 305 | if self._language: # Don't generate event on init 306 | self.event_generate("<>") 307 | 308 | self._language = language 309 | self.highlight_all() 310 | 311 | def _set_lexer(self, lang): 312 | if lang == "ada": 313 | self._lexer = AdaLexer 314 | elif lang == "bash": 315 | self._lexer = BashLexer 316 | elif lang == "batch": 317 | self._lexer = BatchLexer 318 | elif lang in ["brainfuck", "bf"]: 319 | self._lexer = BrainfuckLexer 320 | elif lang == "c": 321 | self._lexer = CLexer 322 | elif lang == "cmake": 323 | self._lexer = CMakeLexer 324 | elif lang in ["coffeescript", "coffee"]: 325 | self._lexer = CoffeeScriptLexer 326 | elif lang == "css": 327 | self._lexer = CssLexer 328 | elif lang in ["c sharp", "cs", "c#"]: 329 | self._lexer = CSharpLexer 330 | elif lang in ["c plus plus", "cpp", "c++"]: 331 | self._lexer = CppLexer 332 | elif lang == "dart": 333 | self._lexer = DartLexer 334 | elif lang == "delphi": 335 | self._lexer = DelphiLexer 336 | elif lang in ["dockerfile", "docker"]: 337 | self._lexer = DockerLexer 338 | elif lang == "fortran": 339 | self._lexer = FortranLexer 340 | elif lang in ["go", "golang"]: 341 | self._lexer = GoLexer 342 | elif lang == "groovy": 343 | self._lexer = GroovyLexer 344 | elif lang == "haskell": 345 | self._lexer = HaskellLexer 346 | elif lang == "html": 347 | self._lexer = HtmlLexer 348 | elif lang == "java": 349 | self._lexer = JavaLexer 350 | elif lang in ["javascript", "js"]: 351 | self._lexer = JavascriptLexer 352 | elif lang == "json": 353 | self._lexer = JsonLexer 354 | elif lang == "kotlin": 355 | self._lexer = KotlinLexer 356 | elif lang == "lisp": 357 | self._lexer = CommonLispLexer 358 | elif lang == "lua": 359 | self._lexer = LuaLexer 360 | elif lang == "makefile": 361 | self._lexer = MakefileLexer 362 | elif lang == "matlab": 363 | self._lexer = MatlabLexer 364 | elif lang == "nasm": 365 | self._lexer = NasmLexer 366 | elif lang in ["objective-c", "objectivec"]: 367 | self._lexer = ObjectiveCLexer 368 | elif lang == "perl": 369 | self._lexer = PerlLexer 370 | elif lang == "php": 371 | self._lexer = PhpLexer 372 | elif lang == "powershell": 373 | self._lexer = PowerShellLexer 374 | elif lang in ["python", "py"]: 375 | self._lexer = PythonLexer 376 | elif lang in ["r", "erlang"]: 377 | self._lexer = ErlangLexer 378 | elif lang == "ruby": 379 | self._lexer = RubyLexer 380 | elif lang == "swift": 381 | self._lexer = SwiftLexer 382 | elif lang == "sql": 383 | self._lexer = SqlLexer 384 | elif lang == "tcl": 385 | self._lexer = TclLexer 386 | elif lang in ["typescript", "ts"]: 387 | self._lexer = TypeScriptLexer 388 | elif lang == "vim": 389 | self._lexer = VimLexer 390 | elif lang == "yaml": 391 | self._lexer = YamlLexer 392 | 393 | def __setitem__(self, key, value): 394 | self.configure(**{key: value}) 395 | 396 | def __getitem__(self, key: str): 397 | return self.cget(key) 398 | 399 | def __str__(self) -> str: 400 | return self.content 401 | 402 | def __repr__(self) -> str: 403 | result = f"{type(self).__module__}.{type(self).__name__} widget" 404 | 405 | if not self.winfo_exists(): 406 | return f"" 407 | 408 | return f"<{result}, color scheme: {self._highlighter!r}, lexer: {self._lexer.__name__}>" 409 | 410 | def keys(self) -> list: 411 | keys = tk.Text.keys(self) 412 | keys.extend(["autofocus", "highlighter", "language"]) 413 | return sorted(keys) 414 | 415 | def cget(self, key: str): 416 | if key == "highlighter": 417 | return self._highlighter 418 | elif key == "language": 419 | return self._language 420 | else: 421 | return tk.Text.cget(self, key) 422 | 423 | def configure(self, **kwargs) -> None: 424 | lang = kwargs.pop("language", None) 425 | highlighter = kwargs.pop("highlighter", None) 426 | if lang: 427 | self.update_lexer(lang) 428 | if highlighter: 429 | self.update_highlighter(highlighter) 430 | tk.Text.configure(self, **kwargs) 431 | 432 | config = configure 433 | 434 | def pack(self, *args, **kwargs): 435 | self.frame.pack(*args, **kwargs) 436 | 437 | def grid(self, *args, **kwargs): 438 | self.frame.grid(*args, **kwargs) 439 | 440 | def place(self, *args, **kwargs): 441 | self.frame.place(*args, **kwargs) 442 | 443 | def destroy(self): 444 | """Destroys this widget""" 445 | # Explicit tcl calls are needed to avoid recursion error 446 | for i in self.frame.children.values(): 447 | self.tk.call("destroy", i._w) 448 | self.tk.call("destroy", self.frame._w) 449 | -------------------------------------------------------------------------------- /tkcode/codeeditor.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author: rdbende 3 | License: GNU GPLv3 4 | Copyright: 2021 rdbende 5 | """ 6 | 7 | import tkinter as tk 8 | from tkinter import ttk 9 | 10 | from . import codebox 11 | 12 | 13 | class CodeEditor(codebox.BaseCodeBox): 14 | def __init__( 15 | self, 16 | master: tk.Misc, 17 | language="python", 18 | highlighter="mariana", 19 | autofocus=False, 20 | **kwargs, 21 | ): 22 | 23 | codebox.BaseCodeBox.__init__( 24 | self, master, language, highlighter, autofocus, **kwargs 25 | ) 26 | 27 | self.horizontal_scroll = ttk.Scrollbar( 28 | self.frame, orient="horizontal", command=self.xview 29 | ) 30 | self.vertical_scroll = ttk.Scrollbar( 31 | self.frame, orient="vertical", command=self.yview 32 | ) 33 | self.configure( 34 | xscrollcommand=self.horizontal_scroll.set, 35 | yscrollcommand=self.vertical_scroll.set, 36 | ) 37 | 38 | self.horizontal_scroll.grid(row=1, column=0, sticky="ew") 39 | self.vertical_scroll.grid(row=0, column=1, sticky="ns") 40 | 41 | self.ctrl_cmd_key = ( 42 | "Command" if self.tk.call("tk", "windowingsystem") == "aqua" else "Control" 43 | ) 44 | 45 | self.bind("", self.highlight_line, add=True) 46 | self.bind("<>", self.paste, add=True) 47 | self.bind(f"<{self.ctrl_cmd_key}-a>", self.select_all) 48 | try: 49 | self.bind("", self.change_cursor_mode, add=True) 50 | self.bind("", self.change_cursor_mode, add=True) 51 | except tk.TclError: 52 | pass 53 | 54 | def paste(self, *_): 55 | """Handles text pasting""" 56 | if self.tag_ranges("sel"): 57 | sel_start = self.index("sel.first") 58 | self.delete(sel_start, self.index("sel.last")) 59 | self.mark_set("insert", sel_start) 60 | 61 | self.insert(self.current_pos, self.clipboard_get()) 62 | 63 | self.event_generate("<>") 64 | return "break" 65 | 66 | def select_all(self, *_): 67 | """Selects everything""" 68 | self.mark_set("insert", "end") 69 | self.tag_add("sel", "1.0", "end") 70 | self.see("insert") 71 | 72 | self.event_generate("<>") 73 | return "break" 74 | 75 | def change_cursor_mode(self, *_): 76 | """ 77 | Toggles between thin line and block cursor. 78 | Can be a little bit confusing, that some program, 79 | like Vim uses the insert key to switch between the insert 80 | and owerwrite mode, and others, like Sublime, 81 | Gedit, KWrite or even Gtk widgets uses it to change the cursor appearance 82 | """ 83 | self.configure( 84 | blockcursor=not self["blockcursor"], 85 | insertwidth=1 if self["blockcursor"] else 0, 86 | ) 87 | self.event_generate("<>") 88 | return "break" 89 | 90 | @property 91 | def current_line(self) -> int: 92 | return int(self.index("insert").split(".")[0]) 93 | 94 | @current_line.setter 95 | def current_line(self, line_number: int): 96 | self.mark_set("insert", f"{line_number}.0") 97 | self.see(f"{line_number}.0") 98 | 99 | @property 100 | def current_column(self) -> int: 101 | return int(self.index("insert").split(".")[1]) 102 | 103 | @current_column.setter 104 | def current_column(self, col_number: int): 105 | self.mark_set("insert", f"{self.current_line}.{col_number}") 106 | self.see(f"{self.current_line}.{col_number}") 107 | 108 | @property 109 | def current_pos(self) -> str: 110 | return str(self.index("insert")) 111 | 112 | @current_pos.setter 113 | def current_pos(self, position: str) -> None: 114 | self.mark_set("insert", position) 115 | self.see(position) 116 | 117 | @property 118 | def current_linestart(self) -> str: 119 | return str(self.index("insert linestart")) 120 | 121 | @property 122 | def current_lineend(self) -> str: 123 | return str(self.index("insert lineend")) 124 | -------------------------------------------------------------------------------- /tkcode/schemes/azure.json: -------------------------------------------------------------------------------- 1 | { 2 | "general": 3 | { 4 | "background":"#333333", 5 | "foreground": "#ffffff", 6 | "insertbackground": "#ffbd00", 7 | "insertwidth": 1, 8 | "inactiveselectbackground": "#555555", 9 | "highlightthickness": 0, 10 | "borderwidth": 0 11 | }, 12 | 13 | 14 | "selection": 15 | { 16 | "background": "#555555", 17 | "foreground": "#ffffff" 18 | }, 19 | 20 | 21 | "syntax": 22 | { 23 | "Token.Text": "#ffffff", 24 | "Token.Other": "#ffffff", 25 | "Token.Eror": "#ff8058", 26 | "Token.Whitespace": "#ffffff", 27 | "Token.Escape": "#ffffff", 28 | 29 | "Token.Keyword": "#d170e9", 30 | "Token.Keyword.Constant": "#d170e9", 31 | "Token.Keyword.Declaration": "#d170e9", 32 | "Token.Keyword.Namespace": "#d170e9", 33 | "Token.Keyword.Pseudo": "#d170e9", 34 | "Token.Keyword.Reserved": "#d170e9", 35 | "Token.Keyword.Type": "#d170e9", 36 | "Token.Keyword.Other": "#d170e9", 37 | 38 | "Token.Punctuation": "#80cbc4", 39 | 40 | "Token.Name.Attribute": "#58abff", 41 | "Token.Name.Builtin": "#58abff", 42 | "Token.Name.Builtin.Pseudo": "#80cbc4", 43 | "Token.Name.Class": "#ffbd00", 44 | "Token.Name.Constant": "#f8f8f2", 45 | "Token.Name.Decorator": "#007fff", 46 | "Token.Name.Entity": "#ffbd00", 47 | "Token.Name.Exception": "#58abff", 48 | "Token.Name.Function": "#ffbd00", 49 | "Token.Name.Function.Magic": "#58abff", 50 | "Token.Name.Label": "#58abff", 51 | "Token.Name.Namespace": "#58abff", 52 | "Token.Name.Other": "#58abff", 53 | "Token.Name.Tag": "#80cbc4", 54 | "Token.Name.Variable": "#58abff", 55 | "Token.Name.Variable.Class": "#58abff", 56 | "Token.Name.Variable.Global": "#58abff", 57 | "Token.Name.Variable.Instance": "#58abff", 58 | "Token.Name.Variable.Magic": "#58abff", 59 | 60 | "Token.Operator": "#ff8058", 61 | "Token.Operator.Word": "#d170e9", 62 | 63 | "Token.Comment": "#777777", 64 | "Token.Comment.Hashbang": "#777777", 65 | "Token.Comment.Multiline": "#777777", 66 | "Token.Comment.Preproc": "#777777", 67 | "Token.Comment.Preprocfile": "#777777", 68 | "Token.Comment.Single": "#777777", 69 | "Token.Comment.Special": "#777777", 70 | 71 | "Token.Literal.Number.Bin": "#ff8058", 72 | "Token.Literal.Number.Float": "#ff8058", 73 | "Token.Literal.Number.Hex": "#ff8058", 74 | "Token.Literal.Number.Integer": "#ff8058", 75 | "Token.Literal.Number.Integer.Long": "#ff8058", 76 | "Token.Literal.Number.Oct": "#ff8058", 77 | 78 | "Token.Literal.String.Affix": "#afd93a", 79 | "Token.Literal.String.Char": "#afd93a", 80 | "Token.Literal.String.Delimeter": "#afd93a", 81 | "Token.Literal.String.Doc": "#afd93a", 82 | "Token.Literal.String.Double": "#afd93a", 83 | "Token.Literal.String.Escape": "#afd93a", 84 | "Token.Literal.String.Heredoc": "#afd93a", 85 | "Token.Literal.String.Interpol": "#afd93a", 86 | "Token.Literal.String.Other": "#afd93a", 87 | "Token.Literal.String.Regex": "#afd93a", 88 | "Token.Literal.String.Single": "#afd93a", 89 | "Token.Literal.String.Symbol": "#afd93a" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tkcode/schemes/dracula.json: -------------------------------------------------------------------------------- 1 | { 2 | "general": 3 | { 4 | "background":"#282a36", 5 | "foreground": "#f8f8f2", 6 | "insertbackground": "#f8f8f0", 7 | "insertwidth": 1, 8 | "inactiveselectbackground": "#402725", 9 | "highlightthickness": 0, 10 | "borderwidth": 0 11 | }, 12 | 13 | 14 | "selection": 15 | { 16 | "background": "#44475a", 17 | "foreground": "#f8f8f2" 18 | }, 19 | 20 | 21 | "syntax": 22 | { 23 | "Token.Text": "#f8f8f2", 24 | "Token.Other": "#f8f8f2", 25 | "Token.Eror": "#f8f8f0", 26 | "Token.Whitespace": "#f8f8f2", 27 | "Token.Escape": "#f8f8f2", 28 | 29 | "Token.Keyword": "#ff79c6", 30 | "Token.Keyword.Constant": "#bd93f9", 31 | "Token.Keyword.Declaration": "#ff79c6", 32 | "Token.Keyword.Namespace": "#ff79c6", 33 | "Token.Keyword.Pseudo": "#ff79c6", 34 | "Token.Keyword.Reserved": "#ff79c6", 35 | "Token.Keyword.Type": "#ff79c6", 36 | "Token.Keyword.Other": "#ff79c6", 37 | 38 | "Token.Punctuation": "#ff79c6", 39 | 40 | "Token.Name.Attribute": "#50fa7b", 41 | 42 | "Token.Name.Builtin.Pseudo": { 43 | "foreground": "#ffb86c", 44 | "font": { 45 | "bold": true 46 | } 47 | }, 48 | "Token.Name.Class": { 49 | "foreground": "#8be9fd", 50 | "font": { 51 | "underline": true 52 | } 53 | }, 54 | "Token.Name.Constant": "#bd93f9", 55 | "Token.Name.Decorator": "#8be9fd", 56 | "Token.Name.Entity": "#50fa7b", 57 | "Token.Name.Exception": "#8be9fd", 58 | "Token.Name.Function": "#50fa7b", 59 | "Token.Name.Function.Magic": "#50fa7b", 60 | "Token.Name.Label": "#50fa7b", 61 | "Token.Name.Namespace": "#f8f8f2", 62 | "Token.Name.Other": "#50fa7b", 63 | "Token.Name.Tag": "#ff79c6", 64 | "Token.Name.Variable": "#ffb86c", 65 | "Token.Name.Variable.Class": "#f8f8f2", 66 | "Token.Name.Variable.Global": "#f8f8f2", 67 | "Token.Name.Variable.Instance": "#f8f8f2", 68 | "Token.Name.Variable.Magic": "#f8f8f2", 69 | 70 | "Token.Operator": "#ff79c6", 71 | "Token.Operator.Word": "#ff79c6", 72 | 73 | "Token.Comment": "#6272a4", 74 | "Token.Comment.Hashbang": "#6272a4", 75 | "Token.Comment.Multiline": "#6272a4", 76 | "Token.Comment.Preproc": "#6272a4", 77 | "Token.Comment.Preprocfile": "#6272a4", 78 | "Token.Comment.Single": "#6272a4", 79 | "Token.Comment.Special": "#6272a4", 80 | 81 | "Token.Literal.Number.Bin": "#bd93f9", 82 | "Token.Literal.Number.Float": "#bd93f9", 83 | "Token.Literal.Number.Hex": "#bd93f9", 84 | "Token.Literal.Number.Integer": "#bd93f9", 85 | "Token.Literal.Number.Integer.Long": "#bd93f9", 86 | "Token.Literal.Number.Oct": "#bd93f9", 87 | 88 | "Token.Literal.String.Affix": "#f1fa8c", 89 | "Token.Literal.String.Char": "#f1fa8c", 90 | "Token.Literal.String.Delimeter": "#f1fa8c", 91 | "Token.Literal.String.Doc": "#f1fa8c", 92 | "Token.Literal.String.Double": "#f1fa8c", 93 | "Token.Literal.String.Escape": "#f1fa8c", 94 | "Token.Literal.String.Heredoc": "#f1fa8c", 95 | "Token.Literal.String.Interpol": "#f1fa8c", 96 | "Token.Literal.String.Other": "#f1fa8c", 97 | "Token.Literal.String.Regex": "#f1fa8c", 98 | "Token.Literal.String.Single": "#f1fa8c", 99 | "Token.Literal.String.Symbol": "#f1fa8c" 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /tkcode/schemes/mariana.json: -------------------------------------------------------------------------------- 1 | { 2 | "general": 3 | { 4 | "background":"#343d46", 5 | "foreground": "#d8dee9", 6 | "insertbackground": "#f9ae58", 7 | "insertwidth": 1, 8 | "inactiveselectbackground": "#4e5a65", 9 | "highlightthickness": 0, 10 | "borderwidth": 0 11 | }, 12 | 13 | 14 | "selection": 15 | { 16 | "background": "#4e5a65", 17 | "foreground": "#d8dee9" 18 | }, 19 | 20 | 21 | "syntax": 22 | { 23 | "Token.Text": "#d8dee9", 24 | "Token.Other": "#d8dee9", 25 | "Token.Eror": "#f9ae58", 26 | "Token.Whitespace": "#d8dee9", 27 | "Token.Escape": "#d8dee9", 28 | 29 | "Token.Keyword": "#c695c6", 30 | "Token.Keyword.Constant": "#c695c6", 31 | "Token.Keyword.Declaration": "#c695c6", 32 | "Token.Keyword.Namespace": "#c695c6", 33 | "Token.Keyword.Pseudo": "#c695c6", 34 | "Token.Keyword.Reserved": "#c695c6", 35 | "Token.Keyword.Type": "#c695c6", 36 | "Token.Keyword.Other": "#c695c6", 37 | 38 | "Token.Punctuation": "#5fb4b4", 39 | 40 | "Token.Name.Attribute": "#c695c6", 41 | "Token.Name.Builtin": "#6699cc", 42 | "Token.Name.Builtin.Pseudo": "#ec5f66", 43 | "Token.Name.Class": "#f9ae58", 44 | "Token.Name.Constant": "#d8dee9", 45 | "Token.Name.Decorator": "#6699cc", 46 | "Token.Name.Entity": "#6699cc", 47 | "Token.Name.Exception": "#6699cc", 48 | "Token.Name.Function": "#5fb4b4", 49 | "Token.Name.Function.Magic": "#6699cc", 50 | "Token.Name.Label": "#6699cc", 51 | "Token.Name.Namespace": "#d8dee9", 52 | "Token.Name.Other": "#6699cc", 53 | "Token.Name.Tag": "#f97b58", 54 | "Token.Name.Variable": "#5fb4b4", 55 | "Token.Name.Variable.Class": "#d8dee9", 56 | "Token.Name.Variable.Global": "#d8dee9", 57 | "Token.Name.Variable.Instance": "#d8dee9", 58 | "Token.Name.Variable.Magic": "#d8dee9", 59 | 60 | "Token.Operator": "#f97b58", 61 | "Token.Operator.Word": "#f97b58", 62 | 63 | "Token.Comment": "#a6acb9", 64 | "Token.Comment.Hashbang": "#a6acb9", 65 | "Token.Comment.Multiline": "#a6acb9", 66 | "Token.Comment.Preproc": "#a6acb9", 67 | "Token.Comment.Preprocfile": "#a6acb9", 68 | "Token.Comment.Single": "#a6acb9", 69 | "Token.Comment.Special": "#a6acb9", 70 | 71 | "Token.Literal.Number.Bin": "#f9ae58", 72 | "Token.Literal.Number.Float": "#f9ae58", 73 | "Token.Literal.Number.Hex": "#f9ae58", 74 | "Token.Literal.Number.Integer": "#f9ae58", 75 | "Token.Literal.Number.Integer.Long": "#f9ae58", 76 | "Token.Literal.Number.Oct": "#f9ae58", 77 | 78 | "Token.Literal.String.Affix": "#99c794", 79 | "Token.Literal.String.Char": "#99c794", 80 | "Token.Literal.String.Delimeter": "#99c794", 81 | "Token.Literal.String.Doc": "#99c794", 82 | "Token.Literal.String.Double": "#99c794", 83 | "Token.Literal.String.Escape": "#99c794", 84 | "Token.Literal.String.Heredoc": "#99c794", 85 | "Token.Literal.String.Interpol": "#99c794", 86 | "Token.Literal.String.Other": "#99c794", 87 | "Token.Literal.String.Regex": "#99c794", 88 | "Token.Literal.String.Single": "#99c794", 89 | "Token.Literal.String.Symbol": "#99c794" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tkcode/schemes/monokai-plus-plus.json: -------------------------------------------------------------------------------- 1 | { 2 | "general": 3 | { 4 | "background":"#1c1c1c", 5 | "foreground": "#cccccc", 6 | "insertbackground": "#f8f8f2", 7 | "insertwidth": 1, 8 | "inactiveselectbackground": "#402725", 9 | "highlightthickness": 0, 10 | "borderwidth": 0 11 | }, 12 | 13 | 14 | "selection": 15 | { 16 | "background": "#59201b", 17 | "foreground": "#f8f8f2" 18 | }, 19 | 20 | 21 | "syntax": 22 | { 23 | "Token.Text": "#f8f8f2", 24 | "Token.Other": "#f8f8f2", 25 | "Token.Eror": "#f92472", 26 | "Token.Whitespace": "#f8f8f2", 27 | "Token.Escape": "#f8f8f2", 28 | 29 | "Token.Keyword": "#f92672", 30 | "Token.Keyword.Constant": "#ae81ff", 31 | "Token.Keyword.Declaration": "#f92672", 32 | "Token.Keyword.Namespace": "#f92672", 33 | "Token.Keyword.Pseudo": "#f92672", 34 | "Token.Keyword.Reserved": "#f92672", 35 | "Token.Keyword.Type": "#2be98a", 36 | "Token.Keyword.Other": "#f92672", 37 | 38 | "Token.Punctuation": "#f92672", 39 | 40 | "Token.Name.Attribute": "#a6e22c", 41 | "Token.Name.Builtin": "#2be98a", 42 | "Token.Name.Builtin.Pseudo": "#ae81ff", 43 | "Token.Name.Class": "#49e0fd", 44 | "Token.Name.Constant": "#ae81ff", 45 | "Token.Name.Decorator": "#67d8ef", 46 | "Token.Name.Entity": "#a6e22c", 47 | "Token.Name.Exception": "#2be98a", 48 | "Token.Name.Function": "#b0ec38", 49 | "Token.Name.Function.Magic": "#b0ec38", 50 | "Token.Name.Label": "#a6e22c", 51 | "Token.Name.Namespace": "#f8f8f2", 52 | "Token.Name.Other": "#a6e22c", 53 | "Token.Name.Tag": "#f92672", 54 | "Token.Name.Variable": "#fd971f", 55 | "Token.Name.Variable.Class": "#a6e22c", 56 | "Token.Name.Variable.Global": "#a6e22c", 57 | "Token.Name.Variable.Instance": "#a6e22c", 58 | "Token.Name.Variable.Magic": "#ae81ff", 59 | 60 | "Token.Operator": "#f92472", 61 | "Token.Operator.Word": "#f92472", 62 | 63 | "Token.Comment": "#696d70", 64 | "Token.Comment.Hashbang": "#696d70", 65 | "Token.Comment.Multiline": "#696d70", 66 | "Token.Comment.Preproc": "#696d70", 67 | "Token.Comment.Preprocfile": "#696d70", 68 | "Token.Comment.Single": "#696d70", 69 | "Token.Comment.Special": "#696d70", 70 | 71 | "Token.Literal.Number.Bin": "#ac80ff", 72 | "Token.Literal.Number.Float": "#ac80ff", 73 | "Token.Literal.Number.Hex": "#ac80ff", 74 | "Token.Literal.Number.Integer": "#ac80ff", 75 | "Token.Literal.Number.Integer.Long": "#ac80ff", 76 | "Token.Literal.Number.Oct": "#ac80ff", 77 | 78 | "Token.Literal.String.Affix": "#e6db74", 79 | "Token.Literal.String.Char": "#e6db74", 80 | "Token.Literal.String.Delimeter": "#e6db74", 81 | "Token.Literal.String.Doc": "#e6db74", 82 | "Token.Literal.String.Double": "#e6db74", 83 | "Token.Literal.String.Escape": "#e6db74", 84 | "Token.Literal.String.Heredoc": "#e6db74", 85 | "Token.Literal.String.Interpol": "#e6db74", 86 | "Token.Literal.String.Other": "#e6db74", 87 | "Token.Literal.String.Regex": "#49e0fd", 88 | "Token.Literal.String.Single": "#e6db74", 89 | "Token.Literal.String.Symbol": "#e6db74" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tkcode/schemes/monokai.json: -------------------------------------------------------------------------------- 1 | { 2 | "general": 3 | { 4 | "background":"#282923", 5 | "foreground": "#f8f8f2", 6 | "insertbackground": "#f8f8f2", 7 | "insertwidth": 1, 8 | "inactiveselectbackground": "#48473d", 9 | "highlightthickness": 0, 10 | "borderwidth": 0 11 | }, 12 | 13 | 14 | "selection": 15 | { 16 | "background": "#48473d", 17 | "foreground": "#f8f8f2" 18 | }, 19 | 20 | 21 | "syntax": 22 | { 23 | "Token.Text": "#f8f8f2", 24 | "Token.Other": "#f8f8f2", 25 | "Token.Eror": "#f92472", 26 | "Token.Whitespace": "#f8f8f2", 27 | "Token.Escape": "#f8f8f2", 28 | 29 | "Token.Keyword": "#f92472", 30 | "Token.Keyword.Constant": "#ac80ff", 31 | "Token.Keyword.Declaration": "#f92472", 32 | "Token.Keyword.Namespace": "#f92472", 33 | "Token.Keyword.Pseudo": "#ac80ff", 34 | "Token.Keyword.Reserved": "#f92472", 35 | "Token.Keyword.Type": "#f92472", 36 | "Token.Keyword.Other": "#f92472", 37 | 38 | "Token.Punctuation": "#f92472", 39 | 40 | "Token.Name.Attribute": "#a6e22c", 41 | "Token.Name.Builtin": "#67d8ef", 42 | "Token.Name.Builtin.Pseudo": { 43 | "foreground": "#fd9621", 44 | "font": { 45 | "bold": true 46 | } 47 | }, 48 | "Token.Name.Class": "#a6e22c", 49 | "Token.Name.Constant": "#f8f8f2", 50 | "Token.Name.Decorator": "#67d8ef", 51 | "Token.Name.Entity": "#a6e22c", 52 | "Token.Name.Exception": "#67d8ef", 53 | "Token.Name.Function": "#a6e22c", 54 | "Token.Name.Function.Magic": "#67d8ef", 55 | "Token.Name.Label": "#a6e22c", 56 | "Token.Name.Namespace": "#f8f8f2", 57 | "Token.Name.Other": "#a6e22c", 58 | "Token.Name.Tag": "#f92472", 59 | "Token.Name.Variable": "#f92472", 60 | "Token.Name.Variable.Class": "#a6e22c", 61 | "Token.Name.Variable.Global": "#a6e22c", 62 | "Token.Name.Variable.Instance": "#a6e22c", 63 | "Token.Name.Variable.Magic": "#a6e22c", 64 | 65 | "Token.Operator": "#f83535", 66 | "Token.Operator.Word": "#f92472", 67 | 68 | "Token.Comment": "#74705d", 69 | "Token.Comment.Hashbang": "#74705d", 70 | "Token.Comment.Multiline": "#74705d", 71 | "Token.Comment.Preproc": "#74705d", 72 | "Token.Comment.Preprocfile": "#74705d", 73 | "Token.Comment.Single": "#74705d", 74 | "Token.Comment.Special": "#74705d", 75 | 76 | "Token.Literal.Number.Bin": "#ac80ff", 77 | "Token.Literal.Number.Float": "#ac80ff", 78 | "Token.Literal.Number.Hex": "#ac80ff", 79 | "Token.Literal.Number.Integer": "#ac80ff", 80 | "Token.Literal.Number.Integer.Long": "#ac80ff", 81 | "Token.Literal.Number.Oct": "#ac80ff", 82 | 83 | "Token.Literal.String.Affix": "#e7db74", 84 | "Token.Literal.String.Char": "#e7db74", 85 | "Token.Literal.String.Delimeter": "#e7db74", 86 | "Token.Literal.String.Doc": "#e7db74", 87 | "Token.Literal.String.Double": "#e7db74", 88 | "Token.Literal.String.Escape": "#e7db74", 89 | "Token.Literal.String.Heredoc": "#e7db74", 90 | "Token.Literal.String.Interpol": "#e7db74", 91 | "Token.Literal.String.Other": "#e7db74", 92 | "Token.Literal.String.Regex": "#e7db74", 93 | "Token.Literal.String.Single": "#e7db74", 94 | "Token.Literal.String.Symbol": "#e7db74" 95 | } 96 | } 97 | --------------------------------------------------------------------------------