Browse Source

chore: added ttkode among the apps

pull/379/head
Parodi, Eugenio 🌶 12 months ago
parent
commit
1ee75d3799
  1. 27
      .github/workflows/release.yml
  2. 3
      .release-please-config.json
  3. 1
      .release-please-manifest.json
  4. 11
      .vscode/launch.json
  5. 3
      README.md
  6. 40
      apps/ttkode/README.md
  7. 46
      apps/ttkode/pyproject.toml
  8. 30
      apps/ttkode/ttkode/__init__.py
  9. 28
      apps/ttkode/ttkode/__main__.py
  10. 29
      apps/ttkode/ttkode/app/__init__.py
  11. 59
      apps/ttkode/ttkode/app/about.py
  12. 56
      apps/ttkode/ttkode/app/cfg.py
  13. 142
      apps/ttkode/ttkode/app/kodeformatter.py
  14. 165
      apps/ttkode/ttkode/app/kodetextdocument.py
  15. 31
      apps/ttkode/ttkode/app/kodetextedit.py
  16. 158
      apps/ttkode/ttkode/app/main.py
  17. 74
      apps/ttkode/ttkode/app/options.py

27
.github/workflows/release.yml

@ -70,13 +70,16 @@ jobs:
_VERSION_TTK=$(jq -r '.["libs/pyTermTk" ]' .release-please-manifest.json)
_VERSION_DPT=$(jq -r '.["apps/dumbPaintTool"]' .release-please-manifest.json)
_VERSION_T_D=$(jq -r '.["apps/ttkDesigner" ]' .release-please-manifest.json)
_VERSION_KOD=$(jq -r '.["apps/ttkode" ]' .release-please-manifest.json)
_NAME_TTK=$(jq -r '.packages["libs/pyTermTk" ]["package-name"]' .release-please-config.json)
_NAME_DPT=$(jq -r '.packages["apps/dumbPaintTool"]["package-name"]' .release-please-config.json)
_NAME_T_D=$(jq -r '.packages["apps/ttkDesigner" ]["package-name"]' .release-please-config.json)
_NAME_KOD=$(jq -r '.packages["apps/ttkode" ]["package-name"]' .release-please-config.json)
echo "Version ${_NAME_TTK}: ${_VERSION_TTK}"
echo "Version ${_NAME_DPT}: ${_VERSION_DPT}"
echo "Version ${_NAME_T_D}: ${_VERSION_T_D}"
echo "Version ${_NAME_KOD}: ${_VERSION_KOD}"
echo '::endgroup::'
echo '::group::Update the Versions'
@ -96,20 +99,23 @@ jobs:
apps/dumbPaintTool/dumbPaintTool/__init__.py
sed "s|'pyTermTk *>=[^']*'|'pyTermTk>=${_VERSION_TTK}'|" -i apps/ttkDesigner/pyproject.toml
fi
if grep -q "${_NAME_KOD}: ${_VERSION_KOD}" <<< ' ${{ steps.release-please.outputs.pr }}': then
sed -i \
"s|__version__:str.*|__version__:str = '${_VERSION_KOD}'|" \
apps/ttkode/ttkode/__init__.py
sed "s|'pyTermTk *>=[^']*'|'pyTermTk>=${_VERSION_TTK}'|" -i apps/ttkode/pyproject.toml
fi
cp libs/pyTermTk/CHANGELOG.md CHANGELOG.md
echo '::endgroup::'
echo '::group::Push the Versions'
git add \
libs/pyTermTk/TermTk/TTkCore/cfg.py \
apps/dumbPaintTool/dumbPaintTool/__init__.py \
apps/ttkDesigner/ttkDesigner/__init__.py \
apps/*/*/__init__.py \
libs/pyTermTk/TermTk/__init__.py \
CHANGELOG.md
find . -name pyproject.toml xargs git add
git commit -m "chore: updated TermTk and apps to versions to ${_VERSION_TTK}, ${_VERSION_DPT}, ${_VERSION_T_D}"
git commit -m "chore: updated TermTk and apps to versions to ${_VERSION_TTK}, ${_VERSION_DPT}, ${_VERSION_T_D} ${_VERSION_KOD}"
git push
echo '::endgroup::'
@ -214,4 +220,15 @@ jobs:
pkg_folder: apps/dumbPaintTool
needs:
- release-please
secrets: inherit
publish-ttkode:
if: ${{ fromJson(needs.release-please.outputs.rp_out)['apps/ttkode--release_created'] }}
name: Publish ttkode
uses: ./.github/workflows/python-publish.yml
with:
pkg_name: ttkode
pkg_folder: apps/ttkode
needs:
- release-please
secrets: inherit

3
.release-please-config.json

@ -23,6 +23,9 @@
},
"apps/ttkDesigner": {
"package-name": "ttkDesigner"
},
"apps/ttkode": {
"package-name": "ttkode"
}
}
}

1
.release-please-manifest.json

@ -1,5 +1,6 @@
{
"libs/pyTermTk": "0.41.17-a.0",
"apps/ttkode": "0.2.14-a.2",
"apps/ttkDesigner": "0.41.4-a.54",
"apps/dumbPaintTool": "0.41.8-a.54"
}

11
.vscode/launch.json vendored

@ -105,6 +105,17 @@
"experiments/untitled.DPT.json"
]
},
{
"name": "py Debug: Module ttkode",
"type": "debugpy",
"request": "launch",
"module": "ttkode",
"console": "integratedTerminal",
"justMyCode": true,
"env": {
"PYTHONPATH": "./apps/ttkode"
}
},
{
"name": "Python: Demo",
"type": "debugpy",

3
README.md

@ -54,6 +54,9 @@ Be inspired by [the Tutorials](https://github.com/ceccopierangiolieugenio/pyTerm
## [Api Definitions](https://ceccopierangiolieugenio.github.io/pyTermTk-Docs/index.html#api-reference)
Don't get bored by the [Api Definitions](https://ceccopierangiolieugenio.github.io/pyTermTk-Docs/index.html#api-reference)
## [ttkode](https://github.com/ceccopierangiolieugenio/pyTermTk/tree/main/apps/ttkode)
Burn your fingers with the Terminal Studio Kode
## [ttkDesigner](https://github.com/ceccopierangiolieugenio/pyTermTk/tree/main/apps/ttkDesigner)
Smell deliciousness with the official [pyTermTk](https://github.com/ceccopierangiolieugenio/pyTermTk) tool for designing and building Text-based user interfaces ([TUI](https://en.wikipedia.org/wiki/Text-based_user_interface)s)

40
apps/ttkode/README.md

@ -0,0 +1,40 @@
![Linux](https://img.shields.io/badge/-Linux-grey?logo=linux)
![Usage](https://img.shields.io/badge/Usage-Terminal%20User%20Interface-yellow)
![Python](https://img.shields.io/badge/Python-v3.8%5E-green?logo=python)
![ttkode_version](https://img.shields.io/github/v/tag/ceccopierangiolieugenio/ttkode?label=version)
[![pypi_version](https://img.shields.io/pypi/v/ttkode?label=pypi)](https://pypi.org/project/ttkode)
[![pypi_version](https://img.shields.io/twitter/follow/Pier95886803?style=social&logo=twitter)](https://twitter.com/hashtag/pyTermTk?src=hashtag_click&f=live)
# ttkode
TerminalToolKit (Studio) Code (editor)
A hopefully fast and mesmerizingly advanced [text-based](https://en.wikipedia.org/wiki/Text-based_user_interface) code editor inspired by [vscode](https://code.visualstudio.com)
## Features (TBD)
- Search Panel
- Highlight
- Bookmarks
- Shiny ASCII Red Peppers
[![screenshot](https://raw.githubusercontent.com/ceccopierangiolieugenio/binaryRepo/master/TTKode/ttkode.0.0.0.gif)](https://pypi.org/project/tlogg)
[Peek 2022-10-08 22-25.webm](https://user-images.githubusercontent.com/8876552/195099208-65d4707e-0340-4077-835a-87ae6c8ae3b6.webm)
# Install from [pypi](https://pypi.org/project/ttkode)
```bash
pip install ttkode
```
# QuickRun
```bash
$ ttkode -h
usage: ttkode [-h] [-c C] path [path ...]
positional arguments:
path the dir/filename/s
optional arguments:
-h, --help show this help message and exit
-c C config folder (default: "/home/user/.config/ttkode")
```

46
apps/ttkode/pyproject.toml

@ -0,0 +1,46 @@
[build-system]
requires = ["setuptools>=45", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "ttkode"
dynamic = ["version"]
readme = {file = "README.md", content-type = "text/markdown"}
authors = [
{name = "Eugenio Parodi", email = "ceccopierangiolieugenio@googlemail.com"},
]
description = "Terminal ToolKit Studio Code editor"
requires-python = ">=3.9"
license = {text = "MIT"}
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Development Status :: 3 - Alpha",
"Environment :: Console",
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"Topic :: Terminals",
"Topic :: Software Development :: User Interfaces",
]
dependencies = [
'pyTermTk>=0.41.17-a.0',
'appdirs',
'copykitten',
'pygments'
]
[project.urls]
Homepage = "https://github.com/ceccopierangiolieugenio/pyTermTk/tree/main/apps/ttkode"
Repository = "https://github.com/ceccopierangiolieugenio/pyTermTk.git"
Issues = "https://github.com/ceccopierangiolieugenio/pyTermTk/issues"
Changelog = "https://github.com/ceccopierangiolieugenio/pyTermTk/blob/main/apps/ttkDesigner/CHANGELOG.md"
[project.scripts]
ttkode = "ttkode:main"
[tool.setuptools]
packages = ["ttkode", "ttkode.app"]
[tool.setuptools.dynamic]
version = {attr = "ttkode.__version__"}

30
apps/ttkode/ttkode/__init__.py

@ -0,0 +1,30 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
__version__:str = '0.2.4-a.15'
from .app import *
if __name__ == "__main__":
main()

28
apps/ttkode/ttkode/__main__.py

@ -0,0 +1,28 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from .app import main
if __name__ == '__main__':
main()

29
apps/ttkode/ttkode/app/__init__.py

@ -0,0 +1,29 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from .cfg import *
# from .glbl import *
from .main import *
from .kodetextdocument import KodeTextDocument

59
apps/ttkode/ttkode/app/about.py

@ -0,0 +1,59 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from TermTk.TTkCore.log import TTkLog
from TermTk.TTkCore.color import TTkColor
from TermTk import TTkAbout, TTkWindow
from .cfg import TTKodeCfg
class About(TTkAbout):
ttkode = [
"__________________ __ ",
"\_______________ / / / ┌─┐ ",
" /\ /\ | |__/ /___ __| |_____ ",
" | | | | | _ // _ \ / _ | ___ |",
" | | | | | | \ \ |_| ( (_| | ____|",
" | | | | └─┘ \_)___/ \____|_____)",
" | | | | ",
" └──┘ └──┘ ",]
__slots__=('_image')
def __init__(self, *args, **kwargs):
TTkAbout.__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , 'About' )
self.setTitle('[PierCecco Cecco] Eugenio Parodi proudly presents...')
self.resize(56,16)
def paintEvent(self, canvas):
c = [0xFF,0xFF,0xFF]
for y, line in enumerate(About.ttkode):
canvas.drawText(pos=(13,3+y),text=line, color=TTkColor.fg(f'#{c[0]:02X}{c[1]:02X}{c[2]:02X}'))
c[2]-=0x18
c[0]-=0x08
canvas.drawText(pos=(26, 9),text=f" Version: {TTKodeCfg.version}", color=TTkColor.fg('#AAAAFF'))
canvas.drawText(pos=(14,11),text=f"Powered By, pyTermTk")
canvas.drawText(pos=( 2,13),text=f"https://github.com/ceccopierangiolieugenio/ttkode", color=TTkColor.fg('#44FFFF'))
canvas.drawText(pos=( 2,14),text=f"https://github.com/ceccopierangiolieugenio/pyTermTk", color=TTkColor.fg('#44FFFF'))
TTkWindow.paintEvent(self, canvas)

56
apps/ttkode/ttkode/app/cfg.py

@ -0,0 +1,56 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os
import json
class TTKodeCfg:
version="__VERSION__"
name="__NAME__"
cfgVersion = '1.0'
pathCfg="."
options={}
maxsearches=200
@staticmethod
def save(searches=True, filters=True, colors=True, options=True):
os.makedirs(TTKodeCfg.pathCfg, exist_ok=True)
optionsPath = os.path.join(TTKodeCfg.pathCfg,'options.json')
def writeCfg(path, cfg):
fullCfg = {
'version':TTKodeCfg.cfgVersion,
'cfg':cfg }
# with open(path, 'w') as f:
# json.dump(fullCfg, f, sort_keys=False, default_flow_style=False)
if options: writeCfg(optionsPath, TTKodeCfg.options)
@staticmethod
def load():
optionsPath = os.path.join(TTKodeCfg.pathCfg,'options.json')
# if os.path.exists(optionsPath):
# with open(optionsPath) as f:
# TTKodeCfg.options = json.load(f, Loader=json.SafeLoader)['cfg']

142
apps/ttkode/ttkode/app/kodeformatter.py

@ -0,0 +1,142 @@
# MIT License
#
# Copyright (c) 2022 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from pygments.formatter import Formatter
from pygments.token import Keyword, Name, Comment, String, Error, \
Number, Operator, Generic, Token, Whitespace
from TermTk import TTkString, TTkColor, TTkLog
#: Map token types to a tuple of color values for light and dark
#: backgrounds.
TTKODE_COLORS = {
Token: TTkColor.RST, # ('', ''),
Whitespace: TTkColor.fg('#888888') , # ('gray', 'brightblack'),
Comment: TTkColor.fg('#888888') , # ('gray', 'brightblack'),
Comment.Preproc: TTkColor.fg('#00FFFF') , # ('cyan', 'brightcyan'),
Keyword: TTkColor.fg('#0000FF') , # ('blue', 'brightblue'),
Keyword.Type: TTkColor.fg('#00FFFF') , # ('cyan', 'brightcyan'),
Operator.Word: TTkColor.fg('#FF8800') , # ('magenta', 'brightmagenta'),
Name.Builtin: TTkColor.fg('#00FFFF') , # ('cyan', 'brightcyan'),
Name.Function: TTkColor.fg('#00FF00') , # ('green', 'brightgreen'),
Name.Namespace: TTkColor.fg('#00FFFF') , # ('_cyan_', '_brightcyan_'),
Name.Class: TTkColor.fg('#00FF00') , # ('_green_', '_brightgreen_'),
Name.Exception: TTkColor.fg('#00FFFF') , # ('cyan', 'brightcyan'),
Name.Decorator: TTkColor.fg('#888888') , # ('brightblack', 'gray'),
Name.Variable: TTkColor.fg('#888888') , # ('red', 'brightred'),
Name.Constant: TTkColor.fg('#888888') , # ('red', 'brightred'),
Name.Attribute: TTkColor.fg('#00FFFF') , # ('cyan', 'brightcyan'),
Name.Tag: TTkColor.fg('#0000FF') , # ('brightblue', 'brightblue'),
String: TTkColor.fg('#FFFF00') , # ('yellow', 'yellow'),
Number: TTkColor.fg('#0000FF') , # ('blue', 'brightblue'),
Generic.Deleted: TTkColor.fg('#FF0000') , # ('brightred', 'brightred'),
Generic.Inserted: TTkColor.fg('#00FF00') , # ('green', 'brightgreen'),
Generic.Heading: TTkColor.fg('#888888') , # ('**', '**'),
Generic.Subheading: TTkColor.fg('#FF8800') , # ('*magenta*', '*brightmagenta*'),
Generic.Prompt: TTkColor.fg('#888888') , # ('**', '**'),
Generic.Error: TTkColor.fg('#FF0000') , # ('brightred', 'brightred'),
Error: TTkColor.fg('#FF0000') , # ('_brightred_', '_brightred_'),
}
class KodeFormatter(Formatter):
class Data():
__slots__=('lines', 'block', 'error', 'multiline')
def __init__(self, lines, block):
self.lines = lines
self.block = block
self.error = None
self.multiline = False
__slots__ = ('_dl', '_blockNum', '_kodeStyles')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._kodeStyles = {}
self._blockNum = 1
for token, style in self.style:
# Token = Token.Comment.PreprocFile
# style = {
# 'color': '6272a4',
# 'bgcolor': None,
# 'bold': False, 'italic': False, 'underline': False,
# 'border': None,
# 'roman': None, 'sans': None, 'mono': None,
# 'ansicolor': None, 'bgansicolor': None}
# TTkLog.debug(f"{token=} {style=}")
color = TTkColor.RST
if style['color']:
color += TTkColor.fg(f"#{style['color']}")
if style['bgcolor']:
color += TTkColor.bg(f"#{style['bgcolor']}")
if style['bold']:
color += TTkColor.BOLD
if style['italic']:
color += TTkColor.ITALIC
if style['underline']:
color += TTkColor.UNDERLINE
self._kodeStyles[token] = color
super().__init__()
def setDl(self,dl):
self._dl = dl
def format(self, tokensource, _):
multiline = False
multilineId = 0
for ttype, value in tokensource:
if ttype == Error and self._dl.error is None:
self._dl.error = len(self._dl.lines)-1
# self._dl.multiline = ttype == Comment.Multiline
multiline = ttype == Comment.Multiline
while ttype not in self._kodeStyles:
ttype = ttype.parent
# TTkLog.debug (f"{ttype=}")
# TTkLog.debug (f"{value=}")
color = self._kodeStyles[ttype]
values = value.split('\n')
self._dl.lines[-1] += TTkString(values[0],color)
self._dl.lines += [TTkString(t,color) for t in values[1:]]
self._dl.block[-1] = self._blockNum
self._dl.block += [self._blockNum]*(len(values)-1)
# self._dl.lines += [TTkString(t) for t in value.split('\n')]
# multiline = len(values)>1 if self._dl.lines[-1]._text == values[-1] else self._dl.multiline
# if self._dl.lines[-1]._text == '' or not multiline:
# self._blockNum += 1
# multilineId = len(self._dl.lines)
if multiline:
multilineId += len(values)
else:
multilineId = 0
self._blockNum += 1
if multiline:
self._dl.multiline = multilineId

165
apps/ttkode/ttkode/app/kodetextdocument.py

@ -0,0 +1,165 @@
# MIT License
#
# Copyright (c) 2022 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from threading import Lock
from pygments import highlight
from pygments.util import ClassNotFound
from pygments.lexers import guess_lexer, guess_lexer_for_filename, special
from pygments.formatters import TerminalFormatter, Terminal256Formatter, TerminalTrueColorFormatter
from TermTk import TTk, TTkK, TTkLog, TTkCfg, TTkTheme, TTkTerm, TTkHelper, TTkTimer
from TermTk import TTkString
from TermTk import TTkColor, TTkColorGradient
from TermTk import pyTTkSlot, pyTTkSignal
from TermTk import TTkTextDocument
from .kodeformatter import KodeFormatter
class KodeTextDocument(TTkTextDocument):
_linesRefreshed = 30
__slots__ = (
'_filePath', '_timerRefresh',
'kodeHighlightUpdate', '_kodeDocMutex',
'_blocks', '_changedContent', '_refreshContent',
'_lexer', '_formatter')
def __init__(self, filePath:str="", **kwargs):
self.kodeHighlightUpdate = pyTTkSignal()
self._kodeDocMutex = Lock()
self._lexer = None
self._blocks = []
# self._formatter = KodeFormatter(style='dracula')
self._filePath = filePath
self._formatter = KodeFormatter(style='gruvbox-dark')
self._timerRefresh = TTkTimer()
super().__init__(**kwargs)
self._changedContent = (0,0,len(self._dataLines))
self._refreshContent = (0,KodeTextDocument._linesRefreshed)
self._timerRefresh.timeout.connect(self._refreshEvent)
self._timerRefresh.start(0.3)
self.contentsChange.connect(lambda a,b,c: TTkLog.debug(f"{a=} {b=} {c=}"))
self.contentsChange.connect(self._saveChangedContent)
@pyTTkSlot(int,int,int)
def _saveChangedContent(self,a,b,c):
if self._changedContent:
self._changedContent = TTkTextDocument._mergeChangesSlices(self._changedContent,(a,b,c))
else:
self._changedContent = (a,b,c)
if not self._refreshContent:
self._refreshContent = (self._changedContent[0], KodeTextDocument._linesRefreshed)
self._timerRefresh.start(0.1)
@pyTTkSlot()
def _refreshEvent(self):
if not self._refreshContent: return
self._kodeDocMutex.acquire()
ra,rb = self._refreshContent
if self._changedContent:
ca,cb,cc = self._changedContent
self._changedContent = None
self._blocks[ca:ca+cb] = [0]*cc
ra = min(ra,ca)
# find the beginning of the current block
# TTkLog.debug(self._blocks)
if ra and self._blocks:
blockId = self._blocks[ra]
for i,v in enumerate(reversed(self._blocks[:ra])):
# TTkLog.debug(f"{i=}:{v=} {blockId=}")
if v == blockId or not blockId:
blockId = v
ra -= 1
rb += 1
else:
break
# TTkLog.debug(f"{ra=} {rb=}")
eof = False
if (ra+rb) >= len(self._dataLines):
rb = len(self._dataLines)-ra
eof=True
tsl = self._dataLines[ra:ra+rb]
# Find the offset from the first not empty line
# because pygments autostrip the heading empty lines
offset = 0
for i,l in enumerate(tsl):
if l != '':
offset = i
break
rawl = [l._text for l in tsl[offset:]]
rawt = '\n'.join(rawl)
if not self._lexer:
try:
self._lexer = guess_lexer_for_filename(self._filePath, rawt)
except ClassNotFound:
self._lexer = special.TextLexer()
# TTkLog.debug(f"Refresh {self._lexer.name} {ra=} {rb=}")
tsl1 = [TTkString()]*(offset+1)
block = [0]*(offset+1)
kfd = KodeFormatter.Data(tsl1, block)
self._formatter.setDl(kfd)
highlight(rawt, self._lexer, self._formatter)
# for ll in tsl:
# TTkLog.debug(f"1: -{ll}-")
# for ll in tsl1:
# TTkLog.debug(f"2: -{ll}-")
tsl1 = tsl1[:rb]
block = block[:rb]
self._dataLines[ra:ra+rb] = tsl1 + tsl[len(tsl1):]
self._blocks[ra:ra+rb] = block + [-1]*(rb-len(block))
# TTkLog.debug(self._blocks)
if kfd.error is not None:
self._refreshContent = (ra+kfd.error,rb<<1)
# TTkLog.debug(f"Error: {self._refreshContent=}")
elif kfd.multiline is not None:
self._refreshContent = (ra+kfd.multiline,rb<<1)
elif (ra+rb) < len(self._dataLines):
self._refreshContent = (ra+rb,KodeTextDocument._linesRefreshed)
else:
self._refreshContent = None
# TTkLog.debug(f"{self._refreshContent=}")
if not eof:
self._timerRefresh.start(0.03)
else:
TTkLog.debug(f"Refresh {self._lexer.name} DONE!!!")
self._kodeDocMutex.release()
self.kodeHighlightUpdate.emit()
def getLock(self):
return self._kodeDocMutex
def filePath(self):
return self._filePath

31
apps/ttkode/ttkode/app/kodetextedit.py

@ -0,0 +1,31 @@
# MIT License
#
# Copyright (c) 2022 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from TermTk import TTkLog
from TermTk import TTkTextEditView
class KodeTextEditView(TTkTextEditView):
def keyEvent(self, evt) -> bool:
self.document().getLock().acquire()
ret = super().keyEvent(evt)
self.document().getLock().release()
return ret

158
apps/ttkode/ttkode/app/main.py

@ -0,0 +1,158 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os
import re
import sys
import argparse
import appdirs
from pygments import highlight
from pygments.lexers import PythonLexer
from pygments.formatters import TerminalFormatter, Terminal256Formatter, TerminalTrueColorFormatter
from TermTk import TTk, TTkK, TTkLog, TTkCfg, TTkColor, TTkTheme, TTkTerm, TTkHelper
from TermTk import TTkString
from TermTk import TTkColorGradient
from TermTk import pyTTkSlot, pyTTkSignal
from TermTk import TTkFrame, TTkButton
from TermTk import TTkTabWidget, TTkKodeTab
from TermTk import TTkAbstractScrollArea, TTkAbstractScrollView
from TermTk import TTkFileDialogPicker
from TermTk import TTkFileTree, TTkTextEdit
from TermTk import TTkGridLayout
from TermTk import TTkSplitter
from .cfg import *
from .about import *
# from .options import optionsFormLayout, optionsLoadTheme
from .kodetextedit import KodeTextEditView
from .kodetextdocument import KodeTextDocument
class TTKode(TTkGridLayout):
__slots__ = ('_kodeTab', '_documents')
def __init__(self, *, files, **kwargs):
self._documents = {}
super().__init__(**kwargs)
self.addWidget(splitter := TTkSplitter())
layoutLeft = TTkGridLayout()
splitter.addItem(layoutLeft, 20)
hSplitter = TTkSplitter(parent=splitter, orientation=TTkK.HORIZONTAL)
menuFrame = TTkFrame(border=False, maxHeight=1)
self._kodeTab = TTkKodeTab(parent=hSplitter, border=False, closable=True)
fileMenu = menuFrame.newMenubarTop().addMenu("&File")
fileMenu.addMenu("Open").menuButtonClicked.connect(self._showFileDialog)
fileMenu.addMenu("Close") # .menuButtonClicked.connect(self._closeFile)
fileMenu.addMenu("Exit").menuButtonClicked.connect(lambda _:TTkHelper.quit())
def _showAbout(btn):
TTkHelper.overlay(None, About(), 30,10)
def _showAboutTTk(btn):
TTkHelper.overlay(None, TTkAbout(), 30,10)
helpMenu = menuFrame.newMenubarTop().addMenu("&Help", alignment=TTkK.RIGHT_ALIGN)
helpMenu.addMenu("About ...").menuButtonClicked.connect(_showAbout)
helpMenu.addMenu("About ttk").menuButtonClicked.connect(_showAboutTTk)
fileTree = TTkFileTree(path='.')
layoutLeft.addWidget(menuFrame, 0,0)
layoutLeft.addWidget(fileTree, 1,0)
layoutLeft.addWidget(quitbtn := TTkButton(border=True, text="Quit", maxHeight=3), 2,0)
quitbtn.clicked.connect(TTkHelper.quit)
for file in files:
self._openFile(file)
fileTree.fileActivated.connect(lambda x: self._openFile(x.path()))
pyTTkSlot()
def _showFileDialog(self):
filePicker = TTkFileDialogPicker(pos = (3,3), size=(75,24), caption="Pick Something", path=".", fileMode=TTkK.FileMode.AnyFile ,filter="All Files (*);;Python Files (*.py);;Bash scripts (*.sh);;Markdown Files (*.md)")
filePicker.pathPicked.connect(self._openFile)
TTkHelper.overlay(None, filePicker, 20, 5, True)
def _openFile(self, filePath):
filePath = os.path.realpath(filePath)
if filePath in self._documents:
doc = self._documents[filePath]['doc']
else:
with open(filePath, 'r') as f:
content = f.read()
doc = KodeTextDocument(text=content, filePath=filePath)
self._documents[filePath] = {'doc':doc,'tabs':[]}
tview = KodeTextEditView(document=doc, readOnly=False)
tedit = TTkTextEdit(textEditView=tview, lineNumber=True)
doc.kodeHighlightUpdate.connect(tedit.update)
label = TTkString(TTkCfg.theme.fileIcon.getIcon(filePath),TTkCfg.theme.fileIconColor) + TTkColor.RST + " " + os.path.basename(filePath)
self._kodeTab.addTab(tedit, label)
self._kodeTab.setCurrentWidget(tedit)
# def _closeFile():
# if (index := KodeTab.lastUsed.currentIndex()) >= 0:
# KodeTab.lastUsed.removeTab(index)
def main():
TTKodeCfg.pathCfg = appdirs.user_config_dir("ttkode")
parser = argparse.ArgumentParser()
# parser.add_argument('-f', help='Full Screen', action='store_true')
parser.add_argument('-c', help=f'config folder (default: "{TTKodeCfg.pathCfg}")', default=TTKodeCfg.pathCfg)
parser.add_argument('filename', type=str, nargs='*',
help='the filename/s')
args = parser.parse_args()
# TTkLog.use_default_file_logging()
TTKodeCfg.pathCfg = args.c
TTkLog.debug(f"Config Path: {TTKodeCfg.pathCfg}")
TTKodeCfg.load()
# if 'theme' not in TTKodeCfg.options:
# TTKodeCfg.options['theme'] = 'NERD'
# optionsLoadTheme(TTKodeCfg.options['theme'])
TTkTheme.loadTheme(TTkTheme.NERD)
root = TTk( layout=TTKode(files=args.filename), title="TTkode",
sigmask=(
# TTkTerm.Sigmask.CTRL_C |
TTkTerm.Sigmask.CTRL_Q |
TTkTerm.Sigmask.CTRL_S |
TTkTerm.Sigmask.CTRL_Z ))
root.mainloop()

74
apps/ttkode/ttkode/app/options.py

@ -0,0 +1,74 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2022 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import copy
from . import TTKodeCfg, TloggGlbl
from TermTk import *
def optionsLoadTheme(theme):
if theme == 'ASCII':
TTkTheme.loadTheme(TTkTheme.ASCII)
elif theme == 'UTF8':
TTkTheme.loadTheme(TTkTheme.UTF8)
elif theme == 'NERD':
TTkTheme.loadTheme(TTkTheme.NERD)
def optionsFormLayout(win):
options = copy.deepcopy(TTKodeCfg.options)
retLayout = TTkGridLayout()
bottomLayout = TTkGridLayout()
themesFrame = TTkFrame(title="Theme", border=True, layout=TTkVBoxLayout(), maxHeight=5, minHeight=5)
# Themes
themesFrame.layout().addWidget(r1 := TTkRadioButton(text="ASCII", name="theme", checked=options['theme'] == 'ASCII'))
themesFrame.layout().addWidget(r2 := TTkRadioButton(text="UTF-8", name="theme", checked=options['theme'] == 'UTF8'))
themesFrame.layout().addWidget(r3 := TTkRadioButton(text="Nerd", name="theme", checked=options['theme'] == 'NERD'))
retLayout.addWidget(themesFrame,0,0)
retLayout.addWidget(TTkSpacer() ,1,0,1,2)
retLayout.addItem(bottomLayout ,2,0,1,2)
bottomLayout.addWidget(applyBtn := TTkButton(text="Apply", border=True, maxHeight=3),0,1)
bottomLayout.addWidget(cancelBtn := TTkButton(text="Cancel", border=True, maxHeight=3),0,2)
bottomLayout.addWidget(okBtn := TTkButton(text="OK", border=True, maxHeight=3),0,3)
def _saveOptions():
if r1.checkState() == TTkK.Checked: options['theme'] = 'ASCII'
if r2.checkState() == TTkK.Checked: options['theme'] = 'UTF8'
if r3.checkState() == TTkK.Checked: options['theme'] = 'NERD'
TTKodeCfg.options = options
TTKodeCfg.save(searches=False, filters=False, colors=False, options=True)
optionsLoadTheme(options['theme'])
TloggGlbl.refreshViews()
TTkHelper.updateAll()
applyBtn.clicked.connect(_saveOptions)
okBtn.clicked.connect(_saveOptions)
okBtn.clicked.connect(win.close)
cancelBtn.clicked.connect(win.close)
return retLayout
Loading…
Cancel
Save