17 changed files with 898 additions and 5 deletions
@ -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" |
||||
} |
||||
|
||||
@ -0,0 +1,40 @@
|
||||
 |
||||
 |
||||
 |
||||
 |
||||
[](https://pypi.org/project/ttkode) |
||||
[](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 |
||||
|
||||
[](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") |
||||
``` |
||||
|
||||
|
||||
@ -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__"} |
||||
@ -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() |
||||
@ -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() |
||||
@ -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 |
||||
@ -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) |
||||
@ -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'] |
||||
@ -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 |
||||
@ -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 |
||||
@ -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 |
||||
@ -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() |
||||
@ -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…
Reference in new issue