Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect file changes #135

Open
wants to merge 41 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
bbbc209
Create background thread to constantly check the file stats of a give…
aaxu Dec 14, 2017
f27975c
Changed all references to self.isReadOnly to use the FileStats object
aaxu Dec 14, 2017
bde81a9
Modified program to use the FileStats object
aaxu Dec 14, 2017
994a3e3
Fixed calls to start monitoring the correct file one at a time
aaxu Dec 14, 2017
680ff5c
Gave the fileStats object reference to the text buffer so that it can…
aaxu Dec 17, 2017
13514cc
Changed from rendering the topInfo window to rerendering the entire w…
aaxu Dec 20, 2017
5141adc
Changed implementation to make the filestats object talk to the backg…
aaxu Dec 21, 2017
d78721f
Fixed program from crashing due to returning from bg thread early
aaxu Dec 21, 2017
ba39a45
Made filestats thread work correctly and refactored the background fu…
aaxu Dec 21, 2017
1ec739e
Made only one function in fileStats that returns all the data that th…
aaxu Dec 21, 2017
b3295d4
Implemented single-threaded version for fileStats and fixed redo chai…
aaxu Dec 21, 2017
d4df6dc
Fixed synch issue which also fixed the fileStats thread when running …
aaxu Dec 22, 2017
c149754
Add empty line to end of file
aaxu Dec 22, 2017
d0eb68c
Update old comments since arguments were changed after refactoring an…
aaxu Dec 22, 2017
91ddfd1
Fix isSafeToWrite function to utilize the fileStats object
aaxu Dec 22, 2017
c744b86
Fixed comments and moved fileLoad and setting fileStats attributes ba…
aaxu Dec 22, 2017
7cdee9a
Now if renameBuffer is called, the function will also modify the file…
aaxu Dec 22, 2017
e19b73f
Moved the saved file stats to the FileStats object and created a file…
aaxu Dec 22, 2017
6145283
Created new controller and window for popup messages. Still need to f…
aaxu Dec 24, 2017
47c1256
Added basic rendering and integration of the popup window
aaxu Dec 25, 2017
237c802
Fixed the check condition for the FileStats thread to detect file cha…
aaxu Jan 23, 2018
4afcef4
Added check that prevents this function from breaking before some att…
aaxu Dec 25, 2017
502ebcd
Added better support for changing files to track and can check if fil…
aaxu Dec 25, 2017
085addd
Created a new function that checks if a file's content has changed. T…
aaxu Dec 31, 2017
ffcb070
Got popup window to kind of display. Need a lot of polishing
aaxu Jan 1, 2018
799d681
Fixed positioning of the popup window. Need to fix format and colorin…
aaxu Jan 1, 2018
4661eda
Fixed the layout of the popup textbox. Need to fix color and add Y/N …
aaxu Jan 1, 2018
048ad10
Add comment
aaxu Jan 2, 2018
2f575d9
Added options parameter for the popup window, improved the format of …
aaxu Jan 3, 2018
decf88f
Stopped fileStats thread when waiting for user input. Need to make it…
aaxu Jan 3, 2018
105d9ab
Made new functions that check whether file has changed since save and…
aaxu Jan 4, 2018
0c35651
Background output now contains objects of (frame, callerSemaphore). F…
aaxu Jan 4, 2018
48252e6
Changed color of the popup window and added support for 8 color termi…
aaxu Jan 4, 2018
dbce963
Cleaned up code and fixed a bug that would sometimes appear if tempCh…
aaxu Jan 5, 2018
4474831
Overrode setTextBuffer method so that it tells controller when the po…
aaxu Jan 11, 2018
c8de77f
Added copyright comment to file_stats.py
aaxu Jan 18, 2018
0bcfd0c
Merge with updated master
aaxu Jan 18, 2018
cf38871
Readd deleted line which fixes parsing issues and rebased to master
aaxu Jan 22, 2018
bb915ba
Merged with master. Need to test and set popup options for fileStat t…
aaxu Feb 4, 2018
4ec9d8e
Fixed the fileStats thread to work with the new popup window from mas…
aaxu Feb 4, 2018
0c68f39
Merge with master
aaxu Mar 5, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 14 additions & 19 deletions app/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -773,8 +773,6 @@ def fileFilter(self, data):
def fileLoad(self):
app.log.info('fileLoad', self.fullPath)
inputFile = None
self.isReadOnly = (os.path.isfile(self.fullPath) and
not os.access(self.fullPath, os.W_OK))
if not os.path.exists(self.fullPath):
self.setMessage('Creating new file')
else:
Expand All @@ -797,7 +795,7 @@ def fileLoad(self):
app.log.info('error opening file', self.fullPath)
self.setMessage('error opening file', self.fullPath)
return
self.fileStat = os.stat(self.fullPath)
self.fileStats.savedFileStat = self.fileStats.fileStats
self.relativePath = os.path.relpath(self.fullPath, os.getcwd())
app.log.info('fullPath', self.fullPath)
app.log.info('cwd', os.getcwd())
Expand Down Expand Up @@ -833,30 +831,31 @@ def restoreUserHistory(self):
None.
"""
# Restore the file history.
self.fileHistory = app.history.getFileHistory(self.fullPath, self.data)
self.fileHistory = app.history.getFileHistory(self.fileStats, self.data)

# Restore all positions and values of variables.
self.view.cursorRow, self.view.cursorCol = self.fileHistory.setdefault(
'cursor', (0, 0))
self.penRow, self.penCol = self.fileHistory.setdefault('pen', (0, 0))
self.view.scrollRow, self.view.scrollCol = self.fileHistory.setdefault(
'scroll', (0, 0))
self.doSelectionMode(self.fileHistory.setdefault('selectionMode',
app.selectable.kSelectionNone))
self.markerRow, self.markerCol = self.fileHistory.setdefault('marker',
(0, 0))
if app.prefs.editor['saveUndo']:
self.redoChain = self.fileHistory.setdefault('redoChainCompound', [])
self.savedAtRedoIndex = self.fileHistory.setdefault('savedAtRedoIndexCompound', 0)
self.redoIndex = self.savedAtRedoIndex
self.oldRedoIndex = self.savedAtRedoIndex
self.tempChange = None
self.doSelectionMode(self.fileHistory.setdefault('selectionMode',
app.selectable.kSelectionNone))
self.markerRow, self.markerCol = self.fileHistory.setdefault('marker',
(0, 0))

# Restore file bookmarks
self.bookmarks = self.fileHistory.setdefault('bookmarks', [])

# Store the file's info.
self.lastChecksum, self.lastFileSize = app.history.getFileInfo(
self.fullPath)
self.fileStats)

def updateBasicScrollPosition(self):
"""
Expand Down Expand Up @@ -953,7 +952,6 @@ def linesToData(self):
def fileWrite(self):
# Preload the message with an error that should be overwritten.
self.setMessage('Error saving file')
self.isReadOnly = not os.access(self.fullPath, os.W_OK)
try:
try:
if app.prefs.editor['onSaveStripTrailingSpaces']:
Expand All @@ -980,20 +978,17 @@ def fileWrite(self):
if app.prefs.editor['saveUndo']:
self.fileHistory['redoChainCompound'] = self.redoChain
self.fileHistory['savedAtRedoIndexCompound'] = self.savedAtRedoIndex
app.history.saveUserHistory((self.fullPath, self.lastChecksum,
self.lastFileSize), self.fileHistory)
app.history.saveUserHistory((self.lastChecksum, self.lastFileSize),
self.fileStats, self.fileHistory)
# Store the file's new info
self.lastChecksum, self.lastFileSize = app.history.getFileInfo(
self.fullPath)
self.fileStat = os.stat(self.fullPath)
# If we're writing this file for the first time, self.isReadOnly will
# still be True (from when it didn't exist).
self.isReadOnly = False
self.fileStats)
self.fileStats.savedFileStat = self.fileStats.fileStats
self.setMessage('File saved')
except Exception as e:
color = app.color.get('status_line_error')
if self.isReadOnly:
self.setMessage("Permission error. Try modifying in sudo mode.",
if self.fileStats.getUpdatedFileInfo()['isReadOnly']:
self.setMessage("Permission error. Try modifing in sudo mode.",
color=color)
else:
self.setMessage(
Expand Down
36 changes: 26 additions & 10 deletions app/background.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,40 @@ def put(self, data):


def background(inputQueue, outputQueue):
block = True
pid = os.getpid()
signalNumber = signal.SIGUSR1
def redrawProgram(program, callerSemaphore):
"""
Sends a SIGUSR1 signal to the current program and draws its screen.

Args:
program (CiProgram): an instance of the CiProgram object.

Returns:
None.
"""
program.render()
outputQueue.put((app.render.frame.grabFrame(), callerSemaphore))
os.kill(pid, signalNumber)

block = True
while True:
try:
try:
program, message = inputQueue.get(block)
program, message, callerSemaphore = inputQueue.get(block)
#profile = app.profile.beginPythonProfile()
if message == 'quit':
app.log.info('bg received quit message')
return
elif message == 'popup':
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the pop-up simply be a top level modal window in the ci_program.py zOrder, so that background.py wouldn't need to know anything about it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by this? I'm making it so that the FileStats object contacts the background thread, and the background thread contacts the main thread. Right now background.py doesn't really know anything about the pop-up and it's just redirecting it to the main thread.

app.log.meta('bg received popup message')
# assert(callerSemaphore != None)
outputQueue.put((('popup', None), callerSemaphore))
os.kill(pid, signalNumber)
continue
program.executeCommandList(message)
program.focusedWindow.textBuffer.parseScreenMaybe()
program.render()
outputQueue.put(app.render.frame.grabFrame())
os.kill(pid, signalNumber)
redrawProgram(program, callerSemaphore)
#app.profile.endPythonProfile(profile)
if not inputQueue.empty():
continue
Expand All @@ -75,18 +93,16 @@ def background(inputQueue, outputQueue):
program.focusedWindow.textBuffer.parseDocument()
block = len(tb.parser.rows) >= len(tb.lines)
if block:
program.render()
outputQueue.put(app.render.frame.grabFrame())
os.kill(pid, signalNumber)
redrawProgram(program, callerSemaphore)
except Exception as e:
app.log.exception(e)
app.log.error('bg thread exception', e)
errorType, value, tracebackInfo = sys.exc_info()
out = traceback.format_exception(errorType, value, tracebackInfo)
outputQueue.put(('exception', out))
outputQueue.put((('exception', out), None))
os.kill(pid, signalNumber)
while True:
program, message = inputQueue.get()
program, message, callerSemaphore = inputQueue.get()
if message == 'quit':
app.log.info('bg received quit message')
return
Expand Down
10 changes: 10 additions & 0 deletions app/buffer_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ def loadTextBuffer(self, relPath, view):
textBuffer = app.text_buffer.TextBuffer()
textBuffer.setFilePath(fullPath)
textBuffer.view = view
self.renameBuffer(textBuffer, fullPath)
textBuffer.fileStats.setPopupWindow(view.popupWindow)
textBuffer.fileLoad()
self.buffers.append(textBuffer)
if 0:
Expand Down Expand Up @@ -142,6 +144,14 @@ def untrackBuffer_(self, fileBuffer):
app.log.debug(fileBuffer.fullPath)
self.buffers.remove(fileBuffer)

def renameBuffer(self, fileBuffer, fullPath):
"""
For now, when you change the path of a fileBuffer, you should also be
updating its fileStat object, so that it tracks the new file as well.
"""
fileBuffer.fullPath = fullPath
fileBuffer.changeFileStats() # Track this new file.

def fileClose(self, path):
pass

Expand Down
30 changes: 21 additions & 9 deletions app/ci_program.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,21 +102,28 @@ def commandLoop(self):
start = time.time()
# The first render, to get something on the screen.
if useBgThread:
self.bg.put((self.programWindow, []))
self.bg.put((self.programWindow, [], None))
else:
self.render()
# This is the 'main loop'. Execution doesn't leave this loop until the
# application is closing down.
while not self.exiting:
if useBgThread:
while self.bg.hasMessage():
frame = self.bg.get()
frame, callerSemaphore = self.bg.get()
if frame[0] == 'exception':
for line in frame[1]:
userMessage(line[:-1])
self.exiting = True
return
self.refresh(frame[0], frame[1])
if frame[0] == 'popup':
self.programWindow.changeFocusTo(
self.programWindow.inputWindow.popupWindow)
callerSemaphore.release()
else:
self.refresh(frame[0], frame[1])
if callerSemaphore:
callerSemaphore.release()
elif 1:
frame = app.render.frame.grabFrame()
self.refresh(frame[0], frame[1])
Expand Down Expand Up @@ -197,16 +204,21 @@ def commandLoop(self):
ch = app.curses_util.UNICODE_INPUT
if ch == 0 and useBgThread:
# bg response.
frame = None
while self.bg.hasMessage():
frame = self.bg.get()
frame, callerSemaphore = self.bg.get()
if frame[0] == 'exception':
for line in frame[1]:
userMessage(line[:-1])
self.exiting = True
return
if frame is not None:
self.refresh(frame[0], frame[1])
if frame[0] == 'popup':
self.programWindow.changeFocusTo(self.programWindow.
inputWindow.popupWindow)
callerSemaphore.release()
else:
self.refresh(frame[0], frame[1])
if callerSemaphore:
callerSemaphore.release()
elif ch != curses.ERR:
self.ch = ch
if ch == curses.KEY_MOUSE:
Expand All @@ -220,7 +232,7 @@ def commandLoop(self):
start = time.time()
if len(cmdList):
if useBgThread:
self.bg.put((self.programWindow, cmdList))
self.bg.put((self.programWindow, cmdList, None))
else:
self.programWindow.executeCommandList(cmdList)
self.render()
Expand Down Expand Up @@ -376,7 +388,7 @@ def run(self):
else:
self.commandLoop()
if app.prefs.editor['useBgThread']:
self.bg.put((self.programWindow, 'quit'))
self.bg.put((self.programWindow, 'quit', None))
self.bg.join()

def setUpPalette(self):
Expand Down
3 changes: 3 additions & 0 deletions app/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ def changeToPaletteWindow(self):
def changeToPopup(self):
self.findAndChangeTo('popupWindow')

def changeToPopup(self):
self.host.changeFocusTo(self.host.host.popupWindow)

def changeToPrediction(self):
self.findAndChangeTo('interactivePrediction')

Expand Down
23 changes: 9 additions & 14 deletions app/cu_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,23 +378,18 @@ def changeToInputWindow(self):
self.callerSemaphore.release()
self.callerSemaphore = None

def setOptions(self, options):
def reloadBuffer(self):
"""
This function is used to change the options that are displayed in the
popup window as well as their functions.
Reloads the file on disk into the program. This will get rid of all changes
that have been made to the current file. This will also remove all
edit history.

Args:
options (dict): A dictionary mapping keys (ints) to its
corresponding action.

Returns;
None.
TODO: Make this reloading a new change that can be appended
to the redo chain so user can undo out of a reloadBuffer call.
"""
self.commandSet = options

def setTextBuffer(self, textBuffer):
self.textBuffer = textBuffer

mainBuffer = self.view.host.textBuffer
mainBuffer.fileLoad()
self.changeToInputWindow()

class PaletteDialogController(app.controller.Controller):
"""."""
Expand Down
Loading