如何通过双击使NSTextView余额定界符? [英] How to make NSTextView balance delimiters with a double-click?
问题描述
通常有一个文本编辑器来处理代码或其他结构化内容,以平衡某种定界符.当您双击{时,它会选择匹配的},或者类似地用于()对,[]对等.如何在Cocoa/Obj-C的NSTextView
中实现此行为?
It's common to have a text editor for code or other structured content that balances delimiters of some sort; when you double click on a { it selects to the matching }, or similarly for ( ) pairs, [ ] pairs, etc. How can I implement this behavior in NSTextView
in Cocoa/Obj-C?
(我将暂时发布一个答案,因为我对此一无所获,并花费了今天实施解决方案.欢迎提供更好的答案.)
(I will be posting an answer momentarily, since I found nothing on SO about this and spent today implementing a solution. Better answers are welcome.)
附录:
这与此问题不同.有关NSTextField
的信息,主要涉及NSTextField
和字段编辑器的问题. 如果通过将自定义NSTextView
子类替换为字段编辑器来解决该问题,那么该自定义子类当然可以使用此处提供的解决方案;但是可能还有许多其他方法可以解决NSTextField
的问题,并且将自定义NSTextView
子类替换为字段编辑器显然不是解决该问题的正确方法,无论如何,程序员在NSTextView
(这可能是更常见的问题)可能不太关心所有这些NSTextField
和字段编辑器问题.因此,这是一个不同的问题–尽管我将添加该问题的链接到该问题,以作为可能的方向.
This is not the same as this question, which is about NSTextField
and is primarily concerned with NSTextField
and field editor issues. If that question is solved by substituting a custom NSTextView
subclass into the field editor, then that custom subclass could use the solution given here, of course; but there might be many other ways to solve the problem for NSTextField
, and substituting a custom NSTextView
subclass into the field editor is not obviously the right solution to that problem, and in any case a programmer concerned with delimiter balancing in NSTextView
(which is presumably the more common problem) could care less about all of those NSTextField
and field editor issues. So that is a different question – although I will add a link from that question to this one, as one possible direction it could go.
这也与此问题不同,双击时在NSTextView
中单词"的定义.根据 Apple的文档 ,这些是采用不同解决方案的不同问题;对于定界符平衡(此问题),Apple特别建议使用NSTextView
的selectionRangeForProposedRange:granularity:
方法,而对于更改单词的定义(该问题),Apple特别声明selectionRangeForProposedRange:granularity:
方法不应 .
This is also not the same as this question, which is really about changing the definition of a "word" in NSTextView
when a double-click occurs. As per Apple's documentation, these are different problems with different solutions; for delimiter-balancing (this question) Apple specifically recommends the use of NSTextView
's selectionRangeForProposedRange:granularity:
method, whereas for changing the definition of a word (that question) Apple specifically states that the selectionRangeForProposedRange:granularity:
method should not be used.
推荐答案
In their Cocoa Text Architecture Guide (https://developer.apple.com/library/prerelease/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TextEditing/TextEditing.html), Apple suggests subclassing NSTextView
and overriding selectionRangeForProposedRange:granularity:
to achieve this sort of thing; they even say "For example, in a code editor you can provide a delegate that extends a double click on a brace or parenthesis character to its matching delimiter." However, it is not immediately clear how to achieve this, since you want the delimiter match to happen only at after a simple double-click on a delimiter, not after a double-click-drag or even a double-click-hold-release.
我能想到的最好的解决方案包括覆盖mouseDown:
,并做一些关于事务状态的记账工作.也许有一种更简单的方法.我省略了实际上计算分隔符匹配的代码的核心部分.这取决于您要匹配的分隔符,可能存在的语法复杂性(字符串,注释)等等.在我的代码中,我实际上调用了一个令牌生成器来获取令牌流,然后使用它来查找匹配的定界符. YMMV.所以,这就是我所拥有的:
The best solution I could come up with involves overriding mouseDown:
as well, and doing a little bookkeeping about the state of affairs. Maybe there is a simpler way. I've left out the core part of the code where the delimiter match actually gets calculated; that will depend on what delimiters you're matching, what syntactical complexities (strings, comments) might exist, and so forth. In my code I actually call a tokenizer to get a token stream, and I use that to find the matching delimiter. YMMV. So, here's what I've got:
在您的NSTextView
子类界面(或更好的类扩展)中:
In your NSTextView
subclass interface (or class extension, better yet):
// these are used in selectionRangeForProposedRange:granularity:
// to balance delimiters properly
BOOL inEligibleDoubleClick;
NSTimeInterval doubleDownTime;
在您的NSTextView
子类实现中:
- (void)mouseDown:(NSEvent *)theEvent
{
// Start out willing to work with a double-click for delimiter-balancing;
// see selectionRangeForProposedRange:proposedCharRange granularity: below
inEligibleDoubleClick = YES;
[super mouseDown:theEvent];
}
- (NSRange)selectionRangeForProposedRange:(NSRange)proposedCharRange
granularity:(NSSelectionGranularity)granularity
{
if ((granularity == NSSelectByWord) && inEligibleDoubleClick)
{
// The proposed range has to be zero-length to qualify
if (proposedCharRange.length == 0)
{
NSEvent *event = [NSApp currentEvent];
NSEventType eventType = [event type];
NSTimeInterval eventTime = [event timestamp];
if (eventType == NSLeftMouseDown)
{
// This is the mouseDown of the double-click; we do not want
// to modify the selection here, just log the time
doubleDownTime = eventTime;
}
else if (eventType == NSLeftMouseUp)
{
// After the double-click interval since the second mouseDown,
// the mouseUp is no longer eligible
if (eventTime - doubleDownTime <= [NSEvent doubleClickInterval])
{
NSString *scriptString = [[self textStorage] string];
...insert delimiter-finding code here...
...return the matched range, or NSBeep()...
}
else
{
inEligibleDoubleClick = false;
}
}
else
{
inEligibleDoubleClick = false;
}
}
else
{
inEligibleDoubleClick = false;
}
}
return [super selectionRangeForProposedRange:proposedCharRange
granularity:granularity];
}
这有点脆弱,因为它依赖于NSTextView
的跟踪以特定的方式工作并以特定的方式调用selectionRangeForProposedRange:granularity:
,但是假设并不大.我认为它非常健壮.
It's a little fragile, because it relies on NSTextView
's tracking working in a particular way and calling out to selectionRangeForProposedRange:granularity:
in a particular way, but the assumptions are not large; I imagine it's pretty robust.
这篇关于如何通过双击使NSTextView余额定界符?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!