ttkode 0.3.2a2__tar.gz → 0.5.0a2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/PKG-INFO +2 -2
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/pyproject.toml +1 -1
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode/__init__.py +1 -1
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode/__main__.py +1 -5
- ttkode-0.5.0a2/ttkode/app/__init__.py +27 -0
- ttkode-0.3.2a2/ttkode/app/__init__.py → ttkode-0.5.0a2/ttkode/app/state.py +4 -4
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode/app/ttkode.py +195 -41
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode/plugins/_010/findwidget.py +146 -56
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode/proxy.py +17 -13
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode.egg-info/PKG-INFO +2 -2
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode.egg-info/SOURCES.txt +1 -0
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode.egg-info/requires.txt +1 -1
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/README.md +0 -0
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/setup.cfg +0 -0
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode/app/about.py +0 -0
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode/app/activitybar.py +0 -0
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode/app/cfg.py +0 -0
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode/app/options.py +0 -0
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode/helper.py +0 -0
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode/plugin.py +0 -0
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode/plugins/_010/__init__.py +0 -0
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode/plugins/_010_findplugin.py +0 -0
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode/plugins/_020_debugplugin.py +0 -0
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode/plugins/testplugin.py +0 -0
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode/plugins/testplugin1.py +0 -0
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode.egg-info/dependency_links.txt +0 -0
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode.egg-info/entry_points.txt +0 -0
- {ttkode-0.3.2a2 → ttkode-0.5.0a2}/ttkode.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ttkode
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0a2
|
|
4
4
|
Summary: Terminal ToolKit Studio Code editor
|
|
5
5
|
Author-email: Eugenio Parodi <ceccopierangiolieugenio@googlemail.com>
|
|
6
6
|
License: MIT
|
|
@@ -19,7 +19,7 @@ Classifier: Topic :: Terminals
|
|
|
19
19
|
Classifier: Topic :: Software Development :: User Interfaces
|
|
20
20
|
Requires-Python: >=3.9
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
|
-
Requires-Dist: pyTermTk>=0.
|
|
22
|
+
Requires-Dist: pyTermTk>=0.46.0-a.0
|
|
23
23
|
Requires-Dist: appdirs
|
|
24
24
|
Requires-Dist: copykitten
|
|
25
25
|
Requires-Dist: pygments
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
23
|
# SOFTWARE.
|
|
24
24
|
|
|
25
|
-
__version__:str = '0.
|
|
25
|
+
__version__:str = '0.5.0-a.2'
|
|
26
26
|
|
|
27
27
|
from .plugin import TTkodePlugin, TTkodePluginActivity
|
|
28
28
|
from .proxy import TTKodeViewerProxy, ttkodeProxy
|
|
@@ -33,7 +33,6 @@ from TermTk import TTkLog
|
|
|
33
33
|
|
|
34
34
|
from ttkode import TTkodeHelper
|
|
35
35
|
from ttkode import ttkodeProxy
|
|
36
|
-
from ttkode.app.ttkode import TTKode
|
|
37
36
|
from ttkode.app.cfg import TTKodeCfg
|
|
38
37
|
|
|
39
38
|
|
|
@@ -62,10 +61,7 @@ def main():
|
|
|
62
61
|
|
|
63
62
|
TTkodeHelper._loadPlugins()
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
ttkodeProxy.setTTKode(ttkode)
|
|
67
|
-
|
|
68
|
-
root = TTk( layout=ttkode,
|
|
64
|
+
root = TTk( layout=ttkodeProxy.ttkode(),
|
|
69
65
|
title="TTkode",
|
|
70
66
|
mouseTrack=True,
|
|
71
67
|
sigmask=(
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
# MIT License
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
|
|
5
|
+
#
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
# in the Software without restriction, including without limitation the rights
|
|
9
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
# furnished to do so, subject to the following conditions:
|
|
12
|
+
#
|
|
13
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
# copies or substantial portions of the Software.
|
|
15
|
+
#
|
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
# SOFTWARE.
|
|
23
|
+
|
|
24
|
+
from .cfg import *
|
|
25
|
+
from .state import TTKodeState
|
|
26
|
+
# from .glbl import *
|
|
27
|
+
from .ttkode import TTKode, TTKodeWidget
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
# MIT License
|
|
3
2
|
#
|
|
4
3
|
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
|
|
@@ -21,6 +20,7 @@
|
|
|
21
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
21
|
# SOFTWARE.
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
__all__ = ['TTKodeState']
|
|
24
|
+
|
|
25
|
+
class TTKodeState():
|
|
26
|
+
pass
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
# SOFTWARE.
|
|
22
22
|
|
|
23
|
-
__all__ = ['TTKode']
|
|
23
|
+
__all__ = ['TTKode', 'TTKodeWidget']
|
|
24
24
|
|
|
25
25
|
import os
|
|
26
26
|
from typing import List,Tuple,Optional,Any
|
|
@@ -31,6 +31,10 @@ from TermTk.TTkWidgets.tabwidget import _TTkNewTabWidgetDragData
|
|
|
31
31
|
from .about import About
|
|
32
32
|
from .activitybar import TTKodeActivityBar
|
|
33
33
|
|
|
34
|
+
class TTKodeWidget():
|
|
35
|
+
def closeRequested(self, tab:ttk.TTkTabWidget, num:int):
|
|
36
|
+
raise NotImplementedError()
|
|
37
|
+
|
|
34
38
|
class TTKodeFileWidgetItem(ttk.TTkTreeWidgetItem):
|
|
35
39
|
__slots__ = ('_path', '_lineNumber')
|
|
36
40
|
def __init__(self, *args, path:str, lineNumber:int=0, **kwargs) -> None:
|
|
@@ -45,16 +49,24 @@ class TTKodeFileWidgetItem(ttk.TTkTreeWidgetItem):
|
|
|
45
49
|
class _TextDocument(ttk.TextDocumentHighlight):
|
|
46
50
|
__slots__ = ('_filePath', '_tabText',
|
|
47
51
|
'fileChangedStatus', '_changedStatus', '_savedSnapshot')
|
|
48
|
-
def __init__(self, filePath:str="",
|
|
52
|
+
def __init__(self, filePath:str="", **kwargs):
|
|
49
53
|
self.fileChangedStatus:ttk.pyTTkSignal = ttk.pyTTkSignal(bool, _TextDocument)
|
|
50
54
|
self._filePath:str = filePath
|
|
51
|
-
self._tabText = tabText
|
|
52
55
|
self._changedStatus:bool = False
|
|
56
|
+
self._genTabText()
|
|
53
57
|
super().__init__(**kwargs)
|
|
54
58
|
self._savedSnapshot = self.snapshootId()
|
|
55
59
|
self.guessLexerFromFilename(filePath)
|
|
56
60
|
self.contentsChanged.connect(self._handleContentChanged)
|
|
57
61
|
|
|
62
|
+
def _genTabText(self):
|
|
63
|
+
self._tabText = (
|
|
64
|
+
ttk.TTkString(ttk.TTkCfg.theme.fileIcon.getIcon(self._filePath),ttk.TTkCfg.theme.fileIconColor) +
|
|
65
|
+
ttk.TTkColor.RST + " " + os.path.basename(self._filePath) )
|
|
66
|
+
|
|
67
|
+
def isChanged(self) -> bool:
|
|
68
|
+
return self._changedStatus
|
|
69
|
+
|
|
58
70
|
def getTabButtonStyle(self) -> dict:
|
|
59
71
|
if self._changedStatus:
|
|
60
72
|
return {'default':{'closeGlyph':' ● '}}
|
|
@@ -70,17 +82,67 @@ class _TextDocument(ttk.TextDocumentHighlight):
|
|
|
70
82
|
ttk.TTkLog.debug(f"{self.isUndoAvailable()=} == {self._changedStatus=}")
|
|
71
83
|
self.fileChangedStatus.emit(self._changedStatus, self)
|
|
72
84
|
|
|
73
|
-
def save(self):
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
85
|
+
def save(self) -> None:
|
|
86
|
+
try:
|
|
87
|
+
with open(self._filePath, 'w') as f:
|
|
88
|
+
f.write(self.toPlainText())
|
|
89
|
+
self._changedStatus = False
|
|
90
|
+
self._savedSnapshot = self.snapshootId()
|
|
91
|
+
self.fileChangedStatus.emit(self._changedStatus, self)
|
|
92
|
+
except Exception as e:
|
|
93
|
+
ttk.TTkLog.error(f"Error saving file: {e}")
|
|
94
|
+
|
|
95
|
+
def saveToFile(self, fileName:str) -> None:
|
|
96
|
+
self._filePath = fileName
|
|
97
|
+
self._genTabText()
|
|
98
|
+
self.save()
|
|
99
|
+
class _TextEdit(ttk.TTkTextEdit, TTKodeWidget):
|
|
100
|
+
__slots__ = ('docFocussed')
|
|
80
101
|
def __init__(self, **kwargs):
|
|
102
|
+
self.docFocussed = ttk.pyTTkSignal(_TextDocument)
|
|
81
103
|
super().__init__(**kwargs)
|
|
82
104
|
self.cursorPositionChanged.connect(self._positionChanged)
|
|
83
|
-
|
|
105
|
+
self.textEditView().focusChanged.connect(self._handleFocusChanged)
|
|
106
|
+
|
|
107
|
+
@ttk.pyTTkSlot(bool)
|
|
108
|
+
def _handleFocusChanged(self, focus:bool) -> None:
|
|
109
|
+
if focus:
|
|
110
|
+
self.docFocussed.emit(self.document())
|
|
111
|
+
|
|
112
|
+
def closeRequested(self, tab:ttk.TTkTabWidget, num:int):
|
|
113
|
+
from ttkode import ttkodeProxy
|
|
114
|
+
doc = self.document()
|
|
115
|
+
docs = [wid.document() for wid in ttkodeProxy.iterWidgets(_TextEdit) if wid is not self]
|
|
116
|
+
if not doc.isChanged() or doc in docs:
|
|
117
|
+
ttkodeProxy.closeTab(self)
|
|
118
|
+
else:
|
|
119
|
+
pass
|
|
120
|
+
# Do you want to save the changes you made to ""?
|
|
121
|
+
# Your saves will be lost if you don't save them.
|
|
122
|
+
# Save, Don't Save, Cancel
|
|
123
|
+
messageBox = ttk.TTkMessageBox(
|
|
124
|
+
title="🚨 Close? 🚨",
|
|
125
|
+
text=ttk.TTkString(f"Do you want to save the change\nyou made to {os.path.basename(doc._filePath)}?\n\nYour saves will be lost\nif you don't save them."),
|
|
126
|
+
icon=ttk.TTkMessageBox.Icon.Warning,
|
|
127
|
+
standardButtons=
|
|
128
|
+
ttk.TTkMessageBox.StandardButton.Discard|
|
|
129
|
+
ttk.TTkMessageBox.StandardButton.Save|
|
|
130
|
+
ttk.TTkMessageBox.StandardButton.Cancel)
|
|
131
|
+
|
|
132
|
+
@ttk.pyTTkSlot(ttk.TTkMessageBox.StandardButton)
|
|
133
|
+
def _cb(btn):
|
|
134
|
+
if btn == ttk.TTkMessageBox.StandardButton.Save:
|
|
135
|
+
doc.save()
|
|
136
|
+
self.textEditView().focusChanged.clear()
|
|
137
|
+
ttkodeProxy.closeTab(self)
|
|
138
|
+
if btn == ttk.TTkMessageBox.StandardButton.Discard:
|
|
139
|
+
self.textEditView().focusChanged.clear()
|
|
140
|
+
ttkodeProxy.closeTab(self)
|
|
141
|
+
elif btn == ttk.TTkMessageBox.StandardButton.Cancel:
|
|
142
|
+
return
|
|
143
|
+
messageBox.buttonSelected.clear()
|
|
144
|
+
messageBox.buttonSelected.connect(_cb)
|
|
145
|
+
ttk.TTkHelper.overlay(None, messageBox, 5, 5, True)
|
|
84
146
|
|
|
85
147
|
@ttk.pyTTkSlot(ttk.TTkTextCursor)
|
|
86
148
|
def _positionChanged(self, cursor:ttk.TTkTextCursor):
|
|
@@ -106,8 +168,10 @@ class _TextEdit(ttk.TTkTextEdit):
|
|
|
106
168
|
tedit.textCursor().setPosition(line=linenum,pos=0)
|
|
107
169
|
|
|
108
170
|
class TTKode(ttk.TTkGridLayout):
|
|
109
|
-
__slots__ = ('_kodeTab', '_activityBar')
|
|
171
|
+
__slots__ = ('_kodeTab', '_activityBar', '_lastDoc')
|
|
172
|
+
_lastDoc:Optional[_TextDocument]
|
|
110
173
|
def __init__(self, **kwargs):
|
|
174
|
+
self._lastDoc = None
|
|
111
175
|
super().__init__(**kwargs)
|
|
112
176
|
|
|
113
177
|
appTemplate = ttk.TTkAppTemplate(border=False)
|
|
@@ -119,20 +183,22 @@ class TTKode(ttk.TTkGridLayout):
|
|
|
119
183
|
appTemplate.setMenuBar(appMenuBar:=ttk.TTkMenuBarLayout(), ttk.TTkAppTemplate.MAIN)
|
|
120
184
|
fileMenu = appMenuBar.addMenu("&File")
|
|
121
185
|
fileMenu.addMenu("Open").menuButtonClicked.connect(self._showFileDialog)
|
|
186
|
+
fileMenu.addMenu("&Save").menuButtonClicked.connect(self.saveLastDoc)
|
|
187
|
+
fileMenu.addMenu("Save &As...").menuButtonClicked.connect(self.saveLastDocAs)
|
|
122
188
|
fileMenu.addMenu("Close") # .menuButtonClicked.connect(self._closeFile)
|
|
123
|
-
fileMenu.addMenu("Exit").menuButtonClicked.connect(
|
|
189
|
+
fileMenu.addMenu("Exit").menuButtonClicked.connect(self._quit)
|
|
124
190
|
|
|
125
191
|
def _showAbout(btn):
|
|
126
192
|
ttk.TTkHelper.overlay(None, About(), 30,10)
|
|
127
193
|
def _showAboutTTk(btn):
|
|
128
194
|
ttk.TTkHelper.overlay(None, ttk.TTkAbout(), 30,10)
|
|
129
195
|
|
|
130
|
-
appMenuBar.addMenu("&Quit", alignment=ttk.TTkK.RIGHT_ALIGN).menuButtonClicked.connect(
|
|
196
|
+
appMenuBar.addMenu("&Quit", alignment=ttk.TTkK.RIGHT_ALIGN).menuButtonClicked.connect(self._quit)
|
|
131
197
|
helpMenu = appMenuBar.addMenu("&Help", alignment=ttk.TTkK.RIGHT_ALIGN)
|
|
132
198
|
helpMenu.addMenu("About ...").menuButtonClicked.connect(_showAbout)
|
|
133
199
|
helpMenu.addMenu("About ttk").menuButtonClicked.connect(_showAboutTTk)
|
|
134
200
|
|
|
135
|
-
fileTree = ttk.TTkFileTree(path='.', dragDropMode=ttk.TTkK.DragDropMode.AllowDrag)
|
|
201
|
+
fileTree = ttk.TTkFileTree(path='.', dragDropMode=ttk.TTkK.DragDropMode.AllowDrag, selectionMode=ttk.TTkK.SelectionMode.MultiSelection)
|
|
136
202
|
self._activityBar = TTKodeActivityBar()
|
|
137
203
|
self._activityBar.addActivity(name="Explorer", icon=ttk.TTkString("╔██\n╚═╝"), widget=fileTree, select=True)
|
|
138
204
|
|
|
@@ -159,11 +225,88 @@ class TTKode(ttk.TTkGridLayout):
|
|
|
159
225
|
|
|
160
226
|
fileTree.fileActivated.connect(lambda x: self._openFile(x.path()))
|
|
161
227
|
self._kodeTab.tabAdded.connect(self._tabAdded)
|
|
228
|
+
self._kodeTab.kodeTabCloseRequested.connect(self._handleTabCloseRequested)
|
|
229
|
+
|
|
230
|
+
ttk.TTkShortcut(ttk.TTkK.CTRL | ttk.TTkK.Key_S).activated.connect(self.saveLastDoc)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@ttk.pyTTkSlot(_TextDocument)
|
|
234
|
+
def _handleDocFocussed(self, doc:_TextDocument):
|
|
235
|
+
self._lastDoc = doc
|
|
236
|
+
|
|
237
|
+
@ttk.pyTTkSlot()
|
|
238
|
+
def _quit(self):
|
|
239
|
+
from ttkode import ttkodeProxy
|
|
240
|
+
docs = set(os.path.basename(wid.document()._filePath) for wid in ttkodeProxy.iterWidgets(_TextEdit) if wid.document().isChanged())
|
|
241
|
+
if docs:
|
|
242
|
+
messageBox = ttk.TTkMessageBox(
|
|
243
|
+
title="🚨 Quit? 🚨",
|
|
244
|
+
text=ttk.TTkString("Do you want to quit?\nThere are still unsaved documents,\nif you quit those changes will be lost" + ''.join([f"\n - {_d}" for _d in docs])),
|
|
245
|
+
icon=ttk.TTkMessageBox.Icon.Warning,
|
|
246
|
+
standardButtons=
|
|
247
|
+
ttk.TTkMessageBox.StandardButton.Ok|
|
|
248
|
+
ttk.TTkMessageBox.StandardButton.Cancel)
|
|
249
|
+
@ttk.pyTTkSlot(ttk.TTkMessageBox.StandardButton)
|
|
250
|
+
def _cb(btn):
|
|
251
|
+
if btn == ttk.TTkMessageBox.StandardButton.Ok:
|
|
252
|
+
ttk.TTkHelper.quit()
|
|
253
|
+
elif btn == ttk.TTkMessageBox.StandardButton.Cancel:
|
|
254
|
+
return
|
|
255
|
+
messageBox.buttonSelected.clear()
|
|
256
|
+
messageBox.buttonSelected.connect(_cb)
|
|
257
|
+
ttk.TTkHelper.overlay(None, messageBox, 5, 5, True)
|
|
258
|
+
else:
|
|
259
|
+
ttk.TTkHelper.quit()
|
|
260
|
+
|
|
261
|
+
@ttk.pyTTkSlot()
|
|
262
|
+
def saveLastDoc(self):
|
|
263
|
+
if self._lastDoc:
|
|
264
|
+
self._lastDoc.save()
|
|
265
|
+
|
|
266
|
+
@ttk.pyTTkSlot()
|
|
267
|
+
def saveLastDocAs(self):
|
|
268
|
+
if not self._lastDoc:
|
|
269
|
+
return
|
|
270
|
+
def _approveFile(fileName):
|
|
271
|
+
if os.path.exists(fileName):
|
|
272
|
+
@ttk.pyTTkSlot(ttk.TTkMessageBox.StandardButton)
|
|
273
|
+
def _cb(btn):
|
|
274
|
+
if btn == ttk.TTkMessageBox.StandardButton.Save:
|
|
275
|
+
self._lastDoc.saveToFile(fileName)
|
|
276
|
+
elif btn == ttk.TTkMessageBox.StandardButton.Cancel:
|
|
277
|
+
return
|
|
278
|
+
messageBox = ttk.TTkMessageBox(
|
|
279
|
+
text= (
|
|
280
|
+
ttk.TTkString( f'A file named "{os.path.basename(fileName)}" already exists.\nDo you want to replace it?', ttk.TTkColor.BOLD) +
|
|
281
|
+
ttk.TTkString( f'\n\nReplacing it will overwrite its contents.') ),
|
|
282
|
+
icon=ttk.TTkMessageBox.Icon.Warning,
|
|
283
|
+
standardButtons=
|
|
284
|
+
ttk.TTkMessageBox.StandardButton.Discard|
|
|
285
|
+
ttk.TTkMessageBox.StandardButton.Save|
|
|
286
|
+
ttk.TTkMessageBox.StandardButton.Cancel)
|
|
287
|
+
messageBox.buttonSelected.connect(_cb)
|
|
288
|
+
ttk.TTkHelper.overlay(None, messageBox, 5, 5, True)
|
|
289
|
+
else:
|
|
290
|
+
self._lastDoc.saveToFile(fileName)
|
|
291
|
+
filePicker = ttk.TTkFileDialogPicker(
|
|
292
|
+
pos = (3,3), size=(80,30),
|
|
293
|
+
acceptMode=ttk.TTkK.AcceptMode.AcceptSave,
|
|
294
|
+
caption="Save As...",
|
|
295
|
+
path=self._lastDoc._filePath,
|
|
296
|
+
fileMode=ttk.TTkK.FileMode.AnyFile ,
|
|
297
|
+
filter="All Files (*)")
|
|
298
|
+
filePicker.pathPicked.connect(_approveFile)
|
|
299
|
+
ttk.TTkHelper.overlay(None, filePicker, 5, 5, True)
|
|
300
|
+
|
|
301
|
+
@ttk.pyTTkSlot(ttk.TTkTabWidget, int)
|
|
302
|
+
def _handleTabCloseRequested(self, tab:ttk.TTkTabWidget, num:int):
|
|
303
|
+
# tab.removeTab(num)
|
|
304
|
+
tab.widget(num).closeRequested(tab, num)
|
|
162
305
|
|
|
163
306
|
def _getTabButtonFromWidget(self, widget:ttk.TTkWidget) -> ttk.TTkTabButton:
|
|
164
|
-
for
|
|
165
|
-
if
|
|
166
|
-
return
|
|
307
|
+
for kt, index in self._kodeTab.iterItems():
|
|
308
|
+
if kt.widget(index) == widget:
|
|
309
|
+
return kt.tabButton(index)
|
|
167
310
|
return None
|
|
168
311
|
|
|
169
312
|
ttk.pyTTkSlot(ttk.TTkTabWidget, int)
|
|
@@ -180,34 +323,37 @@ class TTKode(ttk.TTkGridLayout):
|
|
|
180
323
|
ttk.TTkHelper.overlay(None, filePicker, 20, 5, True)
|
|
181
324
|
|
|
182
325
|
def _getDocument(self, filePath) -> Tuple[_TextDocument, Optional[_TextEdit]]:
|
|
183
|
-
for
|
|
184
|
-
if issubclass(type(
|
|
185
|
-
doc =
|
|
326
|
+
for kt, index in self._kodeTab.iterItems():
|
|
327
|
+
if issubclass(type(wid:=kt.widget(index)), _TextEdit):
|
|
328
|
+
doc = wid.document()
|
|
186
329
|
if issubclass(type(doc), _TextDocument):
|
|
187
330
|
if filePath == doc._filePath:
|
|
188
|
-
return doc,
|
|
331
|
+
return doc, wid
|
|
189
332
|
with open(filePath, 'r') as f:
|
|
190
333
|
content = f.read()
|
|
191
|
-
|
|
192
|
-
td = _TextDocument(text=content, filePath=filePath, tabText=tabText)
|
|
334
|
+
td = _TextDocument(text=content, filePath=filePath)
|
|
193
335
|
td.fileChangedStatus.connect(self._handleFileChangedStatus)
|
|
194
336
|
return td, None
|
|
195
337
|
|
|
196
338
|
ttk.pyTTkSlot(bool, _TextDocument)
|
|
197
339
|
def _handleFileChangedStatus(self, status:bool, doc:_TextDocument) -> None:
|
|
198
340
|
# ttk.TTkLog.debug(f"Status ({status}) -> {doc._filePath}")
|
|
199
|
-
for
|
|
200
|
-
if issubclass(type(
|
|
201
|
-
if doc ==
|
|
202
|
-
|
|
341
|
+
for kt, index in self._kodeTab.iterItems():
|
|
342
|
+
if issubclass(type(wid:=kt.widget(index)), _TextEdit):
|
|
343
|
+
if doc == wid.document():
|
|
344
|
+
kt.tabButton(index).mergeStyle(doc.getTabButtonStyle())
|
|
345
|
+
kt.tabButton(index).setText(doc._tabText)
|
|
203
346
|
|
|
204
|
-
def _openFile(self, filePath, line:int=0, pos:int=0):
|
|
347
|
+
def _openFile(self, filePath, line:int=0, pos:int=0) -> None:
|
|
205
348
|
filePath = os.path.realpath(filePath)
|
|
349
|
+
if not os.path.isfile(filePath):
|
|
350
|
+
return
|
|
206
351
|
doc, tedit = self._getDocument(filePath=filePath)
|
|
207
352
|
if tedit:
|
|
208
353
|
self._kodeTab.setCurrentWidget(tedit)
|
|
209
354
|
else:
|
|
210
355
|
tedit = _TextEdit(document=doc, readOnly=False, lineNumber=True)
|
|
356
|
+
tedit.docFocussed.connect(self._handleDocFocussed)
|
|
211
357
|
self._kodeTab.addTab(tedit, doc._tabText)
|
|
212
358
|
self._kodeTab.setCurrentWidget(tedit)
|
|
213
359
|
tedit.goToLine(line)
|
|
@@ -215,28 +361,36 @@ class TTKode(ttk.TTkGridLayout):
|
|
|
215
361
|
|
|
216
362
|
def _dropEventProxyFile(self, evt:ttk.TTkDnDEvent):
|
|
217
363
|
data = evt.data()
|
|
218
|
-
|
|
364
|
+
filePaths = []
|
|
365
|
+
linenums = []
|
|
219
366
|
|
|
220
367
|
if ( issubclass(type(data), ttk.TTkTreeWidget._DropTreeData) and data.items ):
|
|
221
368
|
if issubclass(type(data.items[0]), ttk.TTkFileTreeWidgetItem):
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
369
|
+
ftwis:List[ttk.TTkFileTreeWidgetItem] = data.items
|
|
370
|
+
filePaths = [os.path.realpath(_f.path()) for _f in ftwis]
|
|
371
|
+
linenums:List[int] = [0]*len(filePaths)
|
|
225
372
|
elif issubclass(type(data.items[0]), TTKodeFileWidgetItem):
|
|
226
373
|
kfwi:TTKodeFileWidgetItem = data.items[0]
|
|
227
|
-
|
|
228
|
-
|
|
374
|
+
linenums:List[int] = [kfwi.lineNumber()]
|
|
375
|
+
filePaths = [os.path.realpath(kfwi.path())]
|
|
229
376
|
|
|
230
|
-
|
|
377
|
+
newData = []
|
|
378
|
+
for filePath,linenum in zip(filePaths,linenums) :
|
|
379
|
+
if not os.path.isfile(filePath):
|
|
380
|
+
continue
|
|
231
381
|
doc, _ = self._getDocument(filePath=filePath)
|
|
232
382
|
tedit = _TextEdit(document=doc, readOnly=False, lineNumber=True)
|
|
383
|
+
tedit.docFocussed.connect(self._handleDocFocussed)
|
|
233
384
|
tedit.goToLine(linenum)
|
|
234
|
-
newData
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
385
|
+
newData.append(
|
|
386
|
+
_TTkNewTabWidgetDragData(
|
|
387
|
+
widget=tedit,
|
|
388
|
+
label=doc._tabText,
|
|
389
|
+
data=None,
|
|
390
|
+
closable=True
|
|
391
|
+
)
|
|
239
392
|
)
|
|
393
|
+
if newData:
|
|
240
394
|
newEvt = evt.clone()
|
|
241
395
|
newEvt.setData(newData)
|
|
242
396
|
return newEvt
|
|
@@ -27,8 +27,9 @@ import re
|
|
|
27
27
|
import fnmatch
|
|
28
28
|
import mimetypes
|
|
29
29
|
|
|
30
|
-
from
|
|
31
|
-
from
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
from threading import Thread,Event,Lock
|
|
32
|
+
from typing import Generator,List,Tuple,Dict,Optional
|
|
32
33
|
|
|
33
34
|
import TermTk as ttk
|
|
34
35
|
|
|
@@ -70,24 +71,32 @@ def _load_gitignore_patterns(gitignore_path):
|
|
|
70
71
|
return patterns
|
|
71
72
|
return []
|
|
72
73
|
|
|
73
|
-
def
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
def _glob_match_patterns(path, patterns) -> bool:
|
|
75
|
+
if path == '.':
|
|
76
|
+
check_path = ''
|
|
77
|
+
elif path.startswith('./'):
|
|
78
|
+
check_path = path[2:]
|
|
79
|
+
else:
|
|
80
|
+
check_path = path
|
|
81
|
+
return any(f"/{_p}/" in path for _p in patterns if _p) or any(fnmatch.fnmatch(check_path, _p) for _p in patterns if _p)
|
|
78
82
|
|
|
79
|
-
def _custom_walk(directory:str,
|
|
83
|
+
def _custom_walk(directory:str, include_patterns:List[str]=[], exclude_patterns:List[str]=[]) -> Generator[Tuple[str, str], None, None]:
|
|
80
84
|
gitignore_path = os.path.join(directory, '.gitignore')
|
|
81
|
-
|
|
85
|
+
exclude_patterns = exclude_patterns + _load_gitignore_patterns(gitignore_path)
|
|
82
86
|
for entry in sorted(os.listdir(directory)):
|
|
83
87
|
full_path = os.path.join(directory, entry)
|
|
84
|
-
if
|
|
88
|
+
if _glob_match_patterns(full_path, exclude_patterns):
|
|
89
|
+
continue
|
|
90
|
+
if not os.path.exists(full_path):
|
|
85
91
|
continue
|
|
86
92
|
if os.path.isdir(full_path):
|
|
87
93
|
if entry == '.git':
|
|
88
94
|
continue
|
|
89
|
-
yield from _custom_walk(full_path,
|
|
95
|
+
yield from _custom_walk(full_path, include_patterns, exclude_patterns)
|
|
90
96
|
else:
|
|
97
|
+
if include_patterns and not _glob_match_patterns(full_path, include_patterns):
|
|
98
|
+
# ttk.TTkLog.debug(f"{include_patterns=} {full_path=}")
|
|
99
|
+
continue
|
|
91
100
|
yield directory, entry
|
|
92
101
|
|
|
93
102
|
def _walk_with_gitignore(root):
|
|
@@ -95,7 +104,7 @@ def _walk_with_gitignore(root):
|
|
|
95
104
|
gitignore_path = os.path.join(dirpath, '.gitignore')
|
|
96
105
|
patterns = _load_gitignore_patterns(gitignore_path)
|
|
97
106
|
|
|
98
|
-
filenames[:] = [f for f in filenames if not
|
|
107
|
+
filenames[:] = [f for f in filenames if not _glob_match_patterns(os.path.join(dirpath, f), patterns)]
|
|
99
108
|
|
|
100
109
|
yield dirpath, filenames
|
|
101
110
|
|
|
@@ -126,10 +135,16 @@ class _MatchTreeWidgetItem(TTKodeFileWidgetItem):
|
|
|
126
135
|
|
|
127
136
|
class FindWidget(ttk.TTkContainer):
|
|
128
137
|
__slots__ = (
|
|
129
|
-
'_runId'
|
|
138
|
+
'_runId',
|
|
139
|
+
'_replace_data',
|
|
130
140
|
'_results_tree',
|
|
141
|
+
'_search_thread', '_search_stop_event', '_search_lock',
|
|
131
142
|
'_search_le','_replace_le','_files_inc_le','_files_exc_le')
|
|
132
143
|
_runId:int
|
|
144
|
+
_search_lock:Lock
|
|
145
|
+
_search_thread:Optional[Thread]
|
|
146
|
+
_search_stop_event:Event
|
|
147
|
+
_replace_data:Dict
|
|
133
148
|
_results_tree:ttk.TTkTreeWidget
|
|
134
149
|
_search_le:ttk.TTkLineEdit
|
|
135
150
|
_replace_le:ttk.TTkLineEdit
|
|
@@ -137,6 +152,10 @@ class FindWidget(ttk.TTkContainer):
|
|
|
137
152
|
_files_exc_le:ttk.TTkLineEdit
|
|
138
153
|
def __init__(self, **kwargs):
|
|
139
154
|
self._runId = 0
|
|
155
|
+
self._replace_data = {}
|
|
156
|
+
self._search_thread = None
|
|
157
|
+
self._search_lock = Lock()
|
|
158
|
+
self._search_stop_event = Event()
|
|
140
159
|
super().__init__(**kwargs)
|
|
141
160
|
self.setLayout(layout:=ttk.TTkGridLayout())
|
|
142
161
|
|
|
@@ -177,9 +196,20 @@ class FindWidget(ttk.TTkContainer):
|
|
|
177
196
|
self._files_exc_le = ft_excl
|
|
178
197
|
|
|
179
198
|
btn_search.clicked.connect(self._search)
|
|
199
|
+
btn_replace.clicked.connect(self._ask_replace)
|
|
180
200
|
btn_expand.clicked.connect(self._results_tree.expandAll)
|
|
181
201
|
btn_collapse.clicked.connect(self._results_tree.collapseAll)
|
|
202
|
+
|
|
182
203
|
search.returnPressed.connect(self._search)
|
|
204
|
+
replace.returnPressed.connect(self._search)
|
|
205
|
+
ft_incl.returnPressed.connect(self._search)
|
|
206
|
+
ft_excl.returnPressed.connect(self._search)
|
|
207
|
+
|
|
208
|
+
search.textEdited.connect(self._search)
|
|
209
|
+
replace.textEdited.connect(self._search)
|
|
210
|
+
ft_incl.textEdited.connect(self._search)
|
|
211
|
+
ft_excl.textEdited.connect(self._search)
|
|
212
|
+
|
|
183
213
|
res_tree.itemActivated.connect(self._activated)
|
|
184
214
|
|
|
185
215
|
@ttk.pyTTkSlot(str)
|
|
@@ -196,53 +226,113 @@ class FindWidget(ttk.TTkContainer):
|
|
|
196
226
|
line = item.lineNumber()
|
|
197
227
|
ttkodeProxy.openFile(file, line)
|
|
198
228
|
|
|
199
|
-
|
|
200
229
|
@ttk.pyTTkSlot()
|
|
201
|
-
def
|
|
202
|
-
self.
|
|
203
|
-
search_pattern = str(self._search_le.text())
|
|
204
|
-
if not search_pattern:
|
|
230
|
+
def _ask_replace(self) -> None:
|
|
231
|
+
if not self._replace_le.text():
|
|
205
232
|
return
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
233
|
+
_numFiles = len(self._replace_data.get('files',[]))
|
|
234
|
+
_numMatches = sum(len(_r.get('matches',[])) for _r in self._replace_data.get('files',[]))
|
|
235
|
+
_search_pattern = str(self._search_le.text())
|
|
236
|
+
_replace_pattern = str(self._replace_le.text())
|
|
237
|
+
|
|
238
|
+
messageBox = ttk.TTkMessageBox(
|
|
239
|
+
title="🚨 Apply? 🚨",
|
|
240
|
+
text=ttk.TTkString(f"Do you want to repace {_numMatches} occurrences of\n'{_search_pattern}'\nin {_numFiles} files with\n'{_replace_pattern}'?"),
|
|
241
|
+
icon=ttk.TTkMessageBox.Icon.Warning,
|
|
242
|
+
standardButtons=
|
|
243
|
+
ttk.TTkMessageBox.StandardButton.Ok|
|
|
244
|
+
ttk.TTkMessageBox.StandardButton.Cancel)
|
|
245
|
+
|
|
246
|
+
@ttk.pyTTkSlot(ttk.TTkMessageBox.StandardButton)
|
|
247
|
+
def _cb(btn):
|
|
248
|
+
if btn == ttk.TTkMessageBox.StandardButton.Ok:
|
|
249
|
+
ttk.TTkLog.debug(f"Replace '{_search_pattern}' with '{_replace_pattern}'")
|
|
250
|
+
for _file_def in self._replace_data.get('files',[]):
|
|
251
|
+
_file = Path(_file_def['root']) / _file_def['file']
|
|
252
|
+
if not self._replace_data.get('files',[]):
|
|
253
|
+
ttk.TTkLog.error(f"{_file} does not exists!!!")
|
|
254
|
+
else:
|
|
255
|
+
_content = _file.read_text()
|
|
256
|
+
_new_content = _content.replace(_search_pattern,_replace_pattern)
|
|
257
|
+
_file.write_text(_new_content)
|
|
258
|
+
else:
|
|
259
|
+
ttk.TTkLog.debug(f"Discard")
|
|
260
|
+
messageBox.buttonSelected.connect(_cb)
|
|
261
|
+
ttk.TTkHelper.overlay(None, messageBox, 5, 5, True)
|
|
262
|
+
|
|
263
|
+
def _search_threading(self, search_pattern:str, include_patterns:str, exclude_patterns:str, replace_pattern:str) -> None:
|
|
264
|
+
self._results_tree.clear()
|
|
265
|
+
group = []
|
|
266
|
+
groupSize = 1
|
|
267
|
+
self._replace_data = {'files':[]}
|
|
268
|
+
for (file,root,matches) in self._search_files('.',search_pattern,self._runId,include_patterns,exclude_patterns):
|
|
269
|
+
if self._search_stop_event.is_set():
|
|
270
|
+
return
|
|
271
|
+
|
|
272
|
+
self._replace_data['files'].append({'file':file,'root':root,'matches':matches})
|
|
273
|
+
# ttk.TTkLog.debug((file,matches))
|
|
274
|
+
item = ttk.TTkTreeWidgetItem([
|
|
275
|
+
ttk.TTkString(
|
|
276
|
+
ttk.TTkCfg.theme.fileIcon.getIcon(file),
|
|
277
|
+
ttk.TTkCfg.theme.fileIconColor) + " " +
|
|
278
|
+
ttk.TTkString(f" {file} ", ttk.TTkColor.YELLOW+ttk.TTkColor.BOLD+ttk.TTkColor.bg('#000088')) +
|
|
279
|
+
ttk.TTkString(f" {root} ", ttk.TTkColor.fg("#888888"))
|
|
280
|
+
],expanded=True)
|
|
281
|
+
children = []
|
|
282
|
+
for num,line in matches:
|
|
283
|
+
line = line.lstrip(' ')
|
|
284
|
+
# index = line.find(search_pattern)
|
|
285
|
+
# outLine =
|
|
286
|
+
if replace_pattern:
|
|
287
|
+
_s = line.replace('\n','').split(search_pattern)
|
|
288
|
+
_j = (
|
|
289
|
+
ttk.TTkString(search_pattern,ttk.TTkColor.RED + ttk.TTkColor.STRIKETROUGH) +
|
|
290
|
+
ttk.TTkString(replace_pattern,ttk.TTkColor.GREEN) + ttk.TTkColor.RST)
|
|
291
|
+
ttkLine = _j.join(_s)
|
|
292
|
+
else:
|
|
293
|
+
ttkLine = ttk.TTkString(line.replace('\n','')).completeColor(
|
|
294
|
+
match=search_pattern,
|
|
295
|
+
color=ttk.TTkColor.GREEN)
|
|
296
|
+
children.append(_MatchTreeWidgetItem([ttk.TTkString(str(num)+" ",ttk.TTkColor.CYAN) + ttkLine] ,
|
|
297
|
+
match=line,
|
|
298
|
+
lineNumber=num,
|
|
299
|
+
path=os.path.join(root,file)))
|
|
300
|
+
item.addChildren(children)
|
|
301
|
+
group.append(item)
|
|
302
|
+
if len(group) > groupSize:
|
|
240
303
|
self._results_tree.addTopLevelItems(group)
|
|
241
|
-
|
|
304
|
+
group = []
|
|
305
|
+
if groupSize < 0x400:
|
|
306
|
+
groupSize <<= 1
|
|
307
|
+
# self._results_tree.addTopLevelItem(item)
|
|
308
|
+
if group:
|
|
309
|
+
self._results_tree.addTopLevelItems(group)
|
|
242
310
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
311
|
+
@ttk.pyTTkSlot()
|
|
312
|
+
def _search(self) -> None:
|
|
313
|
+
with self._search_lock:
|
|
314
|
+
if self._search_thread:
|
|
315
|
+
self._search_stop_event.set()
|
|
316
|
+
self._search_thread.join()
|
|
317
|
+
self._search_stop_event.clear()
|
|
318
|
+
self._search_thread = None
|
|
319
|
+
self._runId += 1
|
|
320
|
+
search_pattern = str(self._search_le.text())
|
|
321
|
+
replace_pattern = str(self._replace_le.text())
|
|
322
|
+
include_patterns = _s.split(',') if (_s:=str(self._files_inc_le.text())) else []
|
|
323
|
+
exclude_patterns = _s.split(',') if (_s:=str(self._files_exc_le.text())) else []
|
|
324
|
+
if not search_pattern:
|
|
325
|
+
self._results_tree.clear()
|
|
326
|
+
return
|
|
327
|
+
if self._search_thread:
|
|
328
|
+
self._search_thread.join()
|
|
329
|
+
self._search_thread = Thread(
|
|
330
|
+
target=self._search_threading,
|
|
331
|
+
args=(search_pattern, include_patterns, exclude_patterns, replace_pattern))
|
|
332
|
+
self._search_thread.start()
|
|
333
|
+
|
|
334
|
+
def _search_files(self, root_folder, match, runId, include_patterns, exclude_patterns):
|
|
335
|
+
for root, file in _custom_walk(root_folder,include_patterns,exclude_patterns):
|
|
246
336
|
if runId != self._runId:
|
|
247
337
|
return
|
|
248
338
|
if True: # file.endswith('.py'): # file.endswith(file_extension):
|
|
@@ -26,7 +26,7 @@ from typing import Optional, Callable, Any
|
|
|
26
26
|
|
|
27
27
|
import TermTk as ttk
|
|
28
28
|
|
|
29
|
-
from ttkode.app.ttkode import TTKode
|
|
29
|
+
from ttkode.app.ttkode import TTKode, TTKodeWidget
|
|
30
30
|
|
|
31
31
|
class TTKodeViewerProxy():
|
|
32
32
|
__slots__ = ('_fileName')
|
|
@@ -41,26 +41,30 @@ class TTKodeProxy():
|
|
|
41
41
|
'_ttkode',
|
|
42
42
|
# Signals
|
|
43
43
|
)
|
|
44
|
-
_ttkode:
|
|
45
|
-
_openFileCb:
|
|
46
|
-
def __init__(self) -> None:
|
|
47
|
-
self._openFileCb = None
|
|
48
|
-
self._ttkode = None
|
|
49
|
-
|
|
50
|
-
def setTTKode(self, ttkode:TTKode) -> None:
|
|
44
|
+
_ttkode:TTKode
|
|
45
|
+
_openFileCb:Callable[[Any, int, int], Any]
|
|
46
|
+
def __init__(self, ttkode:TTKode) -> None:
|
|
51
47
|
self._ttkode = ttkode
|
|
52
48
|
self._openFileCb = ttkode._openFile
|
|
53
49
|
|
|
54
50
|
def ttkode(self) -> TTKode:
|
|
55
|
-
if not self._ttkode:
|
|
56
|
-
raise Exception("TTkode uninitialized")
|
|
57
51
|
return self._ttkode
|
|
58
52
|
|
|
53
|
+
def iterWidgets(self, widType=TTKodeWidget):
|
|
54
|
+
for kt, index in self._ttkode._kodeTab.iterItems():
|
|
55
|
+
if issubclass(type(wid:=kt.widget(index)), widType):
|
|
56
|
+
yield wid
|
|
57
|
+
|
|
58
|
+
@ttk.pyTTkSlot(TTKodeWidget)
|
|
59
|
+
def closeTab(self, widget:TTKodeWidget) -> None:
|
|
60
|
+
for kt, index in self._ttkode._kodeTab.iterItems():
|
|
61
|
+
if kt.widget(index)==widget:
|
|
62
|
+
kt.removeTab(index)
|
|
63
|
+
|
|
59
64
|
def setOpenFile(self, cb):
|
|
60
65
|
self._openFileCb = cb
|
|
61
66
|
|
|
62
67
|
def openFile(self, fileName:str, line:int=0, pos:int=0):
|
|
63
|
-
|
|
64
|
-
return self._openFileCb(fileName, line, pos)
|
|
68
|
+
return self._openFileCb(fileName, line, pos)
|
|
65
69
|
|
|
66
|
-
ttkodeProxy:TTKodeProxy = TTKodeProxy()
|
|
70
|
+
ttkodeProxy:TTKodeProxy = TTKodeProxy(ttkode=TTKode())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ttkode
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0a2
|
|
4
4
|
Summary: Terminal ToolKit Studio Code editor
|
|
5
5
|
Author-email: Eugenio Parodi <ceccopierangiolieugenio@googlemail.com>
|
|
6
6
|
License: MIT
|
|
@@ -19,7 +19,7 @@ Classifier: Topic :: Terminals
|
|
|
19
19
|
Classifier: Topic :: Software Development :: User Interfaces
|
|
20
20
|
Requires-Python: >=3.9
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
|
-
Requires-Dist: pyTermTk>=0.
|
|
22
|
+
Requires-Dist: pyTermTk>=0.46.0-a.0
|
|
23
23
|
Requires-Dist: appdirs
|
|
24
24
|
Requires-Dist: copykitten
|
|
25
25
|
Requires-Dist: pygments
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|