Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7e1a3ed

Browse files
committed
Merge with upstream
2 parents da07a07 + 7e47520 commit 7e1a3ed

7 files changed

Lines changed: 1022 additions & 607 deletions

File tree

IPython/frontend/cocoa/cocoa_frontend.py

Lines changed: 135 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@
4545
from twisted.internet.threads import blockingCallFromThread
4646
from twisted.python.failure import Failure
4747

48-
#------------------------------------------------------------------------------
48+
#-----------------------------------------------------------------------------
4949
# Classes to implement the Cocoa frontend
50-
#------------------------------------------------------------------------------
50+
#-----------------------------------------------------------------------------
5151

5252
# TODO:
5353
# 1. use MultiEngineClient and out-of-process engine rather than
@@ -61,41 +61,94 @@ def wrapped_execute(self, msg, lines):
6161
"""wrapped_execute"""
6262
try:
6363
p = NSAutoreleasePool.alloc().init()
64-
result = self.shell.execute(lines)
65-
except Exception,e:
66-
# This gives the following:
67-
# et=exception class
68-
# ev=exception class instance
69-
# tb=traceback object
70-
et,ev,tb = sys.exc_info()
71-
# This call adds attributes to the exception value
72-
et,ev,tb = self.shell.formatTraceback(et,ev,tb,msg)
73-
# Add another attribute
74-
75-
# Create a new exception with the new attributes
76-
e = et(ev._ipython_traceback_text)
77-
e._ipython_engine_info = msg
78-
79-
# Re-raise
80-
raise e
64+
result = super(AutoreleasePoolWrappedThreadedEngineService,
65+
self).wrapped_execute(msg, lines)
8166
finally:
8267
p.drain()
8368

8469
return result
8570

86-
def execute(self, lines):
87-
# Only import this if we are going to use this class
88-
from twisted.internet import threads
71+
72+
73+
class Cell(NSObject):
74+
"""
75+
Representation of the prompts, input and output of a cell in the
76+
frontend
77+
"""
78+
79+
blockNumber = objc.ivar().unsigned_long()
80+
blockID = objc.ivar()
81+
inputBlock = objc.ivar()
82+
output = objc.ivar()
83+
84+
85+
86+
class CellBlock(object):
87+
"""
88+
Storage for information about text ranges relating to a single cell
89+
"""
90+
8991

90-
msg = {'engineid':self.id,
91-
'method':'execute',
92-
'args':[lines]}
92+
def __init__(self, inputPromptRange, inputRange=None, outputPromptRange=None,
93+
outputRange=None):
94+
super(CellBlock, self).__init__()
95+
self.inputPromptRange = inputPromptRange
96+
self.inputRange = inputRange
97+
self.outputPromptRange = outputPromptRange
98+
self.outputRange = outputRange
99+
100+
def update_ranges_for_insertion(self, text, textRange):
101+
"""Update ranges for text insertion at textRange"""
102+
103+
for r in [self.inputPromptRange,self.inputRange,
104+
self.outputPromptRange, self.outputRange]:
105+
if(r == None):
106+
continue
107+
intersection = NSIntersectionRange(r,textRange)
108+
if(intersection.length == 0): #ranges don't intersect
109+
if r.location >= textRange.location:
110+
r.location += len(text)
111+
else: #ranges intersect
112+
if(r.location > textRange.location):
113+
offset = len(text) - intersection.length
114+
r.length -= offset
115+
r.location += offset
116+
elif(r.location == textRange.location):
117+
r.length += len(text) - intersection.length
118+
else:
119+
r.length -= intersection.length
120+
121+
122+
def update_ranges_for_deletion(self, textRange):
123+
"""Update ranges for text deletion at textRange"""
93124

94-
d = threads.deferToThread(self.wrapped_execute, msg, lines)
95-
d.addCallback(self.addIDToResult)
96-
return d
125+
for r in [self.inputPromptRange,self.inputRange,
126+
self.outputPromptRange, self.outputRange]:
127+
if(r==None):
128+
continue
129+
intersection = NSIntersectionRange(r, textRange)
130+
if(intersection.length == 0): #ranges don't intersect
131+
if r.location >= textRange.location:
132+
r.location -= textRange.length
133+
else: #ranges intersect
134+
if(r.location > textRange.location):
135+
offset = intersection.length
136+
r.length -= offset
137+
r.location += offset
138+
elif(r.location == textRange.location):
139+
r.length += intersection.length
140+
else:
141+
r.length -= intersection.length
142+
143+
def __repr__(self):
144+
return 'CellBlock('+ str((self.inputPromptRange,
145+
self.inputRange,
146+
self.outputPromptRange,
147+
self.outputRange)) + ')'
148+
97149

98150

151+
99152
class IPythonCocoaController(NSObject, AsyncFrontEndBase):
100153
userNS = objc.ivar() #mirror of engine.user_ns (key=>str(value))
101154
waitingForEngine = objc.ivar().bool()
@@ -120,7 +173,7 @@ def _common_init(self):
120173
self.tabSpaces = 4
121174
self.tabUsesSpaces = True
122175
self.currentBlockID = self.next_block_ID()
123-
self.blockRanges = {} # blockID=>NSRange
176+
self.blockRanges = {} # blockID=>CellBlock
124177

125178

126179
def awakeFromNib(self):
@@ -148,6 +201,7 @@ def awakeFromNib(self):
148201
self.verticalRulerView = r
149202
self.verticalRulerView.setClientView_(self.textView)
150203
self._start_cli_banner()
204+
self.start_new_block()
151205

152206

153207
def appWillTerminate_(self, notification):
@@ -239,14 +293,16 @@ def _store_engine_namespace_values(self, values, keys=[]):
239293

240294

241295
def update_cell_prompt(self, result, blockID=None):
296+
print self.blockRanges
242297
if(isinstance(result, Failure)):
243-
self.insert_text(self.input_prompt(),
244-
textRange=NSMakeRange(self.blockRanges[blockID].location,0),
245-
scrollToVisible=False
246-
)
298+
prompt = self.input_prompt()
299+
247300
else:
248-
self.insert_text(self.input_prompt(number=result['number']),
249-
textRange=NSMakeRange(self.blockRanges[blockID].location,0),
301+
prompt = self.input_prompt(number=result['number'])
302+
303+
r = self.blockRanges[blockID].inputPromptRange
304+
self.insert_text(prompt,
305+
textRange=r,
250306
scrollToVisible=False
251307
)
252308

@@ -255,7 +311,7 @@ def update_cell_prompt(self, result, blockID=None):
255311

256312
def render_result(self, result):
257313
blockID = result['blockID']
258-
inputRange = self.blockRanges[blockID]
314+
inputRange = self.blockRanges[blockID].inputRange
259315
del self.blockRanges[blockID]
260316

261317
#print inputRange,self.current_block_range()
@@ -269,11 +325,17 @@ def render_result(self, result):
269325

270326

271327
def render_error(self, failure):
328+
print failure
329+
blockID = failure.blockID
330+
inputRange = self.blockRanges[blockID].inputRange
272331
self.insert_text('\n' +
273332
self.output_prompt() +
274333
'\n' +
275334
failure.getErrorMessage() +
276-
'\n\n')
335+
'\n\n',
336+
textRange=NSMakeRange(inputRange.location +
337+
inputRange.length,
338+
0))
277339
self.start_new_block()
278340
return failure
279341

@@ -291,22 +353,33 @@ def start_new_block(self):
291353
""""""
292354

293355
self.currentBlockID = self.next_block_ID()
356+
self.blockRanges[self.currentBlockID] = self.new_cell_block()
357+
self.insert_text(self.input_prompt(),
358+
textRange=self.current_block_range().inputPromptRange)
294359

295360

296361

297362
def next_block_ID(self):
298363

299364
return uuid.uuid4()
300365

366+
def new_cell_block(self):
367+
"""A new CellBlock at the end of self.textView.textStorage()"""
368+
369+
return CellBlock(NSMakeRange(self.textView.textStorage().length(),
370+
0), #len(self.input_prompt())),
371+
NSMakeRange(self.textView.textStorage().length(),# + len(self.input_prompt()),
372+
0))
373+
374+
301375
def current_block_range(self):
302376
return self.blockRanges.get(self.currentBlockID,
303-
NSMakeRange(self.textView.textStorage().length(),
304-
0))
377+
self.new_cell_block())
305378

306379
def current_block(self):
307380
"""The current block's text"""
308381

309-
return self.text_for_range(self.current_block_range())
382+
return self.text_for_range(self.current_block_range().inputRange)
310383

311384
def text_for_range(self, textRange):
312385
"""text_for_range"""
@@ -315,7 +388,7 @@ def text_for_range(self, textRange):
315388
return ts.string().substringWithRange_(textRange)
316389

317390
def current_line(self):
318-
block = self.text_for_range(self.current_block_range())
391+
block = self.text_for_range(self.current_block_range().inputRange)
319392
block = block.split('\n')
320393
return block[-1]
321394

@@ -324,38 +397,28 @@ def insert_text(self, string=None, textRange=None, scrollToVisible=True):
324397
"""Insert text into textView at textRange, updating blockRanges
325398
as necessary
326399
"""
327-
328400
if(textRange == None):
329401
#range for end of text
330402
textRange = NSMakeRange(self.textView.textStorage().length(), 0)
331403

332-
for r in self.blockRanges.itervalues():
333-
intersection = NSIntersectionRange(r,textRange)
334-
if(intersection.length == 0): #ranges don't intersect
335-
if r.location >= textRange.location:
336-
r.location += len(string)
337-
else: #ranges intersect
338-
if(r.location <= textRange.location):
339-
assert(intersection.length == textRange.length)
340-
r.length += textRange.length
341-
else:
342-
r.location += intersection.length
343404

344405
self.textView.replaceCharactersInRange_withString_(
345406
textRange, string)
346-
self.textView.setSelectedRange_(
347-
NSMakeRange(textRange.location+len(string), 0))
407+
408+
for r in self.blockRanges.itervalues():
409+
r.update_ranges_for_insertion(string, textRange)
410+
411+
self.textView.setSelectedRange_(textRange)
348412
if(scrollToVisible):
349413
self.textView.scrollRangeToVisible_(textRange)
350-
351414

352415

353416

354417
def replace_current_block_with_string(self, textView, string):
355418
textView.replaceCharactersInRange_withString_(
356-
self.current_block_range(),
357-
string)
358-
self.current_block_range().length = len(string)
419+
self.current_block_range().inputRange,
420+
string)
421+
self.current_block_range().inputRange.length = len(string)
359422
r = NSMakeRange(textView.textStorage().length(), 0)
360423
textView.scrollRangeToVisible_(r)
361424
textView.setSelectedRange_(r)
@@ -424,26 +487,18 @@ def textView_doCommandBySelector_(self, textView, selector):
424487

425488
elif(selector == 'moveToBeginningOfParagraph:'):
426489
textView.setSelectedRange_(NSMakeRange(
427-
self.current_block_range().location,
428-
0))
490+
self.current_block_range().inputRange.location,
491+
0))
429492
return True
430493
elif(selector == 'moveToEndOfParagraph:'):
431494
textView.setSelectedRange_(NSMakeRange(
432-
self.current_block_range().location + \
433-
self.current_block_range().length, 0))
495+
self.current_block_range().inputRange.location + \
496+
self.current_block_range().inputRange.length, 0))
434497
return True
435498
elif(selector == 'deleteToEndOfParagraph:'):
436499
if(textView.selectedRange().location <= \
437500
self.current_block_range().location):
438-
# Intersect the selected range with the current line range
439-
if(self.current_block_range().length < 0):
440-
self.blockRanges[self.currentBlockID].length = 0
441-
442-
r = NSIntersectionRange(textView.rangesForUserTextChange()[0],
443-
self.current_block_range())
444-
445-
if(r.length > 0): #no intersection
446-
textView.setSelectedRange_(r)
501+
raise NotImplemented()
447502

448503
return False # don't actually handle the delete
449504

@@ -457,10 +512,15 @@ def textView_doCommandBySelector_(self, textView, selector):
457512
elif(selector == 'deleteBackward:'):
458513
#if we're at the beginning of the current block, ignore
459514
if(textView.selectedRange().location == \
460-
self.current_block_range().location):
515+
self.current_block_range().inputRange.location):
461516
return True
462517
else:
463-
self.current_block_range().length-=1
518+
for r in self.blockRanges.itervalues():
519+
deleteRange = textView.selectedRange
520+
if(deleteRange.length == 0):
521+
deleteRange.location -= 1
522+
deleteRange.length = 1
523+
r.update_ranges_for_deletion(deleteRange)
464524
return False
465525
return False
466526

@@ -479,14 +539,9 @@ def textView_shouldChangeTextInRanges_replacementStrings_(self,
479539
for r,s in zip(ranges, replacementStrings):
480540
r = r.rangeValue()
481541
if(textView.textStorage().length() > 0 and
482-
r.location < self.current_block_range().location):
542+
r.location < self.current_block_range().inputRange.location):
483543
self.insert_text(s)
484544
allow = False
485-
486-
487-
self.blockRanges.setdefault(self.currentBlockID,
488-
self.current_block_range()).length +=\
489-
len(s)
490545

491546
return allow
492547

0 commit comments

Comments
 (0)