diff --git a/XVim.xcodeproj/project.pbxproj b/XVim.xcodeproj/project.pbxproj index 25410201..3a4718ff 100644 --- a/XVim.xcodeproj/project.pbxproj +++ b/XVim.xcodeproj/project.pbxproj @@ -10,11 +10,19 @@ 6E2B332C1836E42F00EFE4E2 /* XVimBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E2B332B1836E42F00EFE4E2 /* XVimBuffer.m */; }; 6E2B332D1836E42F00EFE4E2 /* XVimBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E2B332B1836E42F00EFE4E2 /* XVimBuffer.m */; }; 6E2B33341836E60600EFE4E2 /* DVTTextStorage+XVimTextStoring.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E2B33331836E60600EFE4E2 /* DVTTextStorage+XVimTextStoring.m */; }; + 6E2C392F183E4BA20056E30F /* XVimView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E2C392E183E4BA20056E30F /* XVimView.m */; }; + 6E2C3930183E4BA20056E30F /* XVimView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E2C392E183E4BA20056E30F /* XVimView.m */; }; + 6E6865D618390702008D5FBB /* XVimUndo.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E6865D518390702008D5FBB /* XVimUndo.m */; }; + 6E6865D718390702008D5FBB /* XVimUndo.m in Sources */ = {isa = PBXBuildFile; fileRef = 6E6865D518390702008D5FBB /* XVimUndo.m */; }; + 6EC4A31E1843A4F5003E2BB5 /* NSCharacterSet+XVimAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EC4A31D1843A4F5003E2BB5 /* NSCharacterSet+XVimAdditions.m */; }; + 6EC4A31F1843A4F5003E2BB5 /* NSCharacterSet+XVimAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EC4A31D1843A4F5003E2BB5 /* NSCharacterSet+XVimAdditions.m */; }; + 6EE2579E183F81C3006E4E1A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A24782C414D6F6DC003B6433 /* InfoPlist.strings */; }; + 6EF220D6183E2A1400B814B8 /* NSObject+XVimAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EF220D5183E2A1400B814B8 /* NSObject+XVimAdditions.m */; }; + 6EF220D7183E2A1400B814B8 /* NSObject+XVimAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6EF220D5183E2A1400B814B8 /* NSObject+XVimAdditions.m */; }; A2126B7516FB30B0000BE21C /* XVimMark.m in Sources */ = {isa = PBXBuildFile; fileRef = A2126B7416FB30B0000BE21C /* XVimMark.m */; }; A2126B7816FB4806000BE21C /* XVimMarks.m in Sources */ = {isa = PBXBuildFile; fileRef = A2126B7716FB4806000BE21C /* XVimMarks.m */; }; A216F3A1156560FE00AD2529 /* IDEEditorArea+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A216F3A0156560FE00AD2529 /* IDEEditorArea+XVim.m */; }; A222B5E11514DFCD005E8802 /* XVimOperatorEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A222B5E01514DFCD005E8802 /* XVimOperatorEvaluator.m */; }; - A2243B7017F03BFC00DD92A1 /* Info_Xcode5.plist in Resources */ = {isa = PBXBuildFile; fileRef = A2E7E45517EFF677008F045A /* Info_Xcode5.plist */; }; A2277ED414F80BCB00A6B70C /* XVimDeleteEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2277ED314F80BCB00A6B70C /* XVimDeleteEvaluator.m */; }; A2277ED814F80BFE00A6B70C /* XVimShiftEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2277ED714F80BFE00A6B70C /* XVimShiftEvaluator.m */; }; A2277EDB14F80D5000A6B70C /* XVimYankEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2277EDA14F80D5000A6B70C /* XVimYankEvaluator.m */; }; @@ -37,12 +45,9 @@ A2575409172F6698003D8A97 /* XVimTester+Mark.m in Sources */ = {isa = PBXBuildFile; fileRef = A2575408172F6698003D8A97 /* XVimTester+Mark.m */; }; A257540C1732A58F003D8A97 /* XVimTester+Visual.m in Sources */ = {isa = PBXBuildFile; fileRef = A257540B1732A58E003D8A97 /* XVimTester+Visual.m */; }; A257C28F156567250098CA09 /* DVTSourceTextView+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A257C28E156567250098CA09 /* DVTSourceTextView+XVim.m */; }; - A2640CC917ACE651003D197D /* NSTextView+VimOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = A2640CC817ACE651003D197D /* NSTextView+VimOperation.m */; }; - A26ACC4E154F2D6800B27D69 /* IDEEditorHook.m in Sources */ = {isa = PBXBuildFile; fileRef = A26ACC4D154F2D6700B27D69 /* IDEEditorHook.m */; }; + A26ACC4E154F2D6800B27D69 /* IDEEditor+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A26ACC4D154F2D6700B27D69 /* IDEEditor+XVim.m */; }; A26DC88615DF33C600779CB4 /* XVimTester.m in Sources */ = {isa = PBXBuildFile; fileRef = A26DC88515DF33C600779CB4 /* XVimTester.m */; }; A26E5F6417A6BCAF0087C9B6 /* NSTextStorage+VimOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = A26E5F6317A6BCAF0087C9B6 /* NSTextStorage+VimOperation.m */; }; - A2702B0216FE537D005ADD76 /* IDEWorkspaceWindowHook.m in Sources */ = {isa = PBXBuildFile; fileRef = A2702B0116FE537D005ADD76 /* IDEWorkspaceWindowHook.m */; }; - A2702B0B16FEA313005ADD76 /* NSObject+ExtraData.m in Sources */ = {isa = PBXBuildFile; fileRef = A2702B0A16FEA313005ADD76 /* NSObject+ExtraData.m */; }; A2771E6B179EF520003B621E /* XVimTester+Issues.m in Sources */ = {isa = PBXBuildFile; fileRef = A2771E6A179EF520003B621E /* XVimTester+Issues.m */; }; A28D42F514FE87AF004BC121 /* XVimGMotionEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A28D42F414FE87AF004BC121 /* XVimGMotionEvaluator.m */; }; A28D42F814FE8E2A004BC121 /* XVimInsertEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A28D42F714FE8E2A004BC121 /* XVimInsertEvaluator.m */; }; @@ -51,7 +56,6 @@ A28F422917EEDBC200A3F7AE /* XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A24782B814D6F56E003B6433 /* XVim.m */; }; A28F422A17EEDBC200A3F7AE /* XVimCommandField.m in Sources */ = {isa = PBXBuildFile; fileRef = A24782BA14D6F56E003B6433 /* XVimCommandField.m */; }; A28F422B17EEDBC200A3F7AE /* XVimCommandLine.m in Sources */ = {isa = PBXBuildFile; fileRef = A2A8A4FF14E41C66002EA6C8 /* XVimCommandLine.m */; }; - A28F422C17EEDBC200A3F7AE /* Hooker.m in Sources */ = {isa = PBXBuildFile; fileRef = A2C4E79714E7F35300751199 /* Hooker.m */; }; A28F422D17EEDBC200A3F7AE /* XVimEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2C4E79B14E7F36B00751199 /* XVimEvaluator.m */; }; A28F422E17EEDBC200A3F7AE /* XVimNormalEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F4A33D14F00B25006DA5A5 /* XVimNormalEvaluator.m */; }; A28F422F17EEDBC200A3F7AE /* XVimVisualEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F4A34214F00BD3006DA5A5 /* XVimVisualEvaluator.m */; }; @@ -67,16 +71,12 @@ A28F423917EEDBC200A3F7AE /* XVimRegisterEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = F100DC2D150BB6BC002C703C /* XVimRegisterEvaluator.m */; }; A28F423A17EEDBC200A3F7AE /* XVimOperatorEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A222B5E01514DFCD005E8802 /* XVimOperatorEvaluator.m */; }; A28F423B17EEDBC200A3F7AE /* XVimKeyStroke.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B2F1526C6B100E1448D /* XVimKeyStroke.m */; }; - A28F423C17EEDBC200A3F7AE /* DVTSourceTextViewHook.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B4015272B0500E1448D /* DVTSourceTextViewHook.m */; }; - A28F423D17EEDBC200A3F7AE /* IDESourceCodeEditorHook.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B4615272DCD00E1448D /* IDESourceCodeEditorHook.m */; }; A28F423E17EEDBC200A3F7AE /* XVimNumericEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B4C1527CEA400E1448D /* XVimNumericEvaluator.m */; }; A28F423F17EEDBC200A3F7AE /* XVimKeymap.m in Sources */ = {isa = PBXBuildFile; fileRef = C366C23A15287EE30008C58E /* XVimKeymap.m */; }; A28F424017EEDBC200A3F7AE /* XVimExCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = A2A736351527484B0051E8E4 /* XVimExCommand.m */; }; A28F424117EEDBC200A3F7AE /* XVimSearch.m in Sources */ = {isa = PBXBuildFile; fileRef = A2A6BA2D152A544B00F0EB5F /* XVimSearch.m */; }; A28F424217EEDBC200A3F7AE /* XVimOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = A2BA3EA1152E372A00C18FB4 /* XVimOptions.m */; }; - A28F424317EEDBC200A3F7AE /* XVimTildeEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AA7225152F186A00C61D97 /* XVimTildeEvaluator.m */; }; - A28F424417EEDBC200A3F7AE /* XVimLowercaseEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AA7229152F1B1700C61D97 /* XVimLowercaseEvaluator.m */; }; - A28F424517EEDBC200A3F7AE /* XVimUppercaseEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AA722C152F1B2700C61D97 /* XVimUppercaseEvaluator.m */; }; + A28F424417EEDBC200A3F7AE /* XVimSwapCharsEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AA7229152F1B1700C61D97 /* XVimSwapCharsEvaluator.m */; }; A28F424617EEDBC200A3F7AE /* XVimWindowEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = F15805C61530B4000031175A /* XVimWindowEvaluator.m */; }; A28F424717EEDBC200A3F7AE /* XVimTextObjectEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C36C104D153131C500CE1D62 /* XVimTextObjectEvaluator.m */; }; A28F424817EEDBC200A3F7AE /* XVimWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = C3FA1A1C1532648700059BF6 /* XVimWindow.m */; }; @@ -87,10 +87,8 @@ A28F424D17EEDBC200A3F7AE /* XVimHistoryHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = C3552DA7153AC6F800D57577 /* XVimHistoryHandler.m */; }; A28F424E17EEDBC200A3F7AE /* XVimMarkSetEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E8F114154267BA007410ED /* XVimMarkSetEvaluator.m */; }; A28F424F17EEDBC200A3F7AE /* XVimArgumentEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E8F11715427AE6007410ED /* XVimArgumentEvaluator.m */; }; - A28F425017EEDBC200A3F7AE /* IDEEditorAreaHook.m in Sources */ = {isa = PBXBuildFile; fileRef = A2ADAB821545C18F0093A908 /* IDEEditorAreaHook.m */; }; A28F425117EEDBC200A3F7AE /* XVimStatusLine.m in Sources */ = {isa = PBXBuildFile; fileRef = A2ABFE1215497A3C002220E8 /* XVimStatusLine.m */; }; - A28F425217EEDBC200A3F7AE /* XVimHookManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C345DDBA154CE12A009F232E /* XVimHookManager.m */; }; - A28F425317EEDBC200A3F7AE /* IDEEditorHook.m in Sources */ = {isa = PBXBuildFile; fileRef = A26ACC4D154F2D6700B27D69 /* IDEEditorHook.m */; }; + A28F425317EEDBC200A3F7AE /* IDEEditor+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A26ACC4D154F2D6700B27D69 /* IDEEditor+XVim.m */; }; A28F425417EEDBC200A3F7AE /* NSInsetTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = C30886591554C8380024161D /* NSInsetTextView.m */; }; A28F425517EEDBC200A3F7AE /* IDEEditorArea+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A216F3A0156560FE00AD2529 /* IDEEditorArea+XVim.m */; }; A28F425617EEDBC200A3F7AE /* DVTSourceTextView+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A257C28E156567250098CA09 /* DVTSourceTextView+XVim.m */; }; @@ -101,8 +99,6 @@ A28F425B17EEDBC200A3F7AE /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = A2C1BB5216CEAC7F0066F420 /* Utils.m */; }; A28F425C17EEDBC200A3F7AE /* XVimMark.m in Sources */ = {isa = PBXBuildFile; fileRef = A2126B7416FB30B0000BE21C /* XVimMark.m */; }; A28F425D17EEDBC200A3F7AE /* XVimMarks.m in Sources */ = {isa = PBXBuildFile; fileRef = A2126B7716FB4806000BE21C /* XVimMarks.m */; }; - A28F425E17EEDBC200A3F7AE /* IDEWorkspaceWindowHook.m in Sources */ = {isa = PBXBuildFile; fileRef = A2702B0116FE537D005ADD76 /* IDEWorkspaceWindowHook.m */; }; - A28F425F17EEDBC200A3F7AE /* NSObject+ExtraData.m in Sources */ = {isa = PBXBuildFile; fileRef = A2702B0A16FEA313005ADD76 /* NSObject+ExtraData.m */; }; A28F426017EEDBC200A3F7AE /* XVimUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = A22A009F1708914D0003046C /* XVimUtil.m */; }; A28F426117EEDBC200A3F7AE /* XVimTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = A22A00A21709CBE50003046C /* XVimTestCase.m */; }; A28F426217EEDBC200A3F7AE /* XVimTester+TextObject.m in Sources */ = {isa = PBXBuildFile; fileRef = A25753FF172F5F0E003D8A97 /* XVimTester+TextObject.m */; }; @@ -116,7 +112,6 @@ A28F426A17EEDBC200A3F7AE /* XVimTester+Recording.m in Sources */ = {isa = PBXBuildFile; fileRef = A2AFD73617914ACE009B442B /* XVimTester+Recording.m */; }; A28F426B17EEDBC200A3F7AE /* XVimTester+Issues.m in Sources */ = {isa = PBXBuildFile; fileRef = A2771E6A179EF520003B621E /* XVimTester+Issues.m */; }; A28F426C17EEDBC200A3F7AE /* NSTextStorage+VimOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = A26E5F6317A6BCAF0087C9B6 /* NSTextStorage+VimOperation.m */; }; - A28F426D17EEDBC200A3F7AE /* NSTextView+VimOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = A2640CC817ACE651003D197D /* NSTextView+VimOperation.m */; }; A28F426E17EEDBC200A3F7AE /* XVimTester+ExCmd.m in Sources */ = {isa = PBXBuildFile; fileRef = A24BE47417AD0D30001F797B /* XVimTester+ExCmd.m */; }; A28F426F17EEDBC200A3F7AE /* XVimTester+Search.m in Sources */ = {isa = PBXBuildFile; fileRef = A28D895617BFF434002709D8 /* XVimTester+Search.m */; }; A28F427017EEDBC200A3F7AE /* XVimJoinEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A23D002317DA0C7D007EBB21 /* XVimJoinEvaluator.m */; }; @@ -128,12 +123,10 @@ A2A736381527484B0051E8E4 /* XVimExCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = A2A736351527484B0051E8E4 /* XVimExCommand.m */; }; A2A8A50014E41C66002EA6C8 /* XVimCommandLine.m in Sources */ = {isa = PBXBuildFile; fileRef = A2A8A4FF14E41C66002EA6C8 /* XVimCommandLine.m */; }; A2ABFE1315497A3C002220E8 /* XVimStatusLine.m in Sources */ = {isa = PBXBuildFile; fileRef = A2ABFE1215497A3C002220E8 /* XVimStatusLine.m */; }; - A2ADAB831545C18F0093A908 /* IDEEditorAreaHook.m in Sources */ = {isa = PBXBuildFile; fileRef = A2ADAB821545C18F0093A908 /* IDEEditorAreaHook.m */; }; A2AFD7341790F300009B442B /* XVimRecordingEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2AFD7331790F2FF009B442B /* XVimRecordingEvaluator.m */; }; A2AFD73717914ACE009B442B /* XVimTester+Recording.m in Sources */ = {isa = PBXBuildFile; fileRef = A2AFD73617914ACE009B442B /* XVimTester+Recording.m */; }; A2BA3EA2152E372A00C18FB4 /* XVimOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = A2BA3EA1152E372A00C18FB4 /* XVimOptions.m */; }; A2C1BB5316CEAC7F0066F420 /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = A2C1BB5216CEAC7F0066F420 /* Utils.m */; }; - A2C4E79814E7F35300751199 /* Hooker.m in Sources */ = {isa = PBXBuildFile; fileRef = A2C4E79714E7F35300751199 /* Hooker.m */; }; A2C4E79C14E7F36B00751199 /* XVimEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2C4E79B14E7F36B00751199 /* XVimEvaluator.m */; }; A2D48B1F15347B5C0088AB71 /* NSString+VimHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = A2D48B1E15347B5C0088AB71 /* NSString+VimHelper.m */; }; A2E7E45717F03113008F045A /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2B4BABB14D59F6600D817B0 /* Cocoa.framework */; }; @@ -141,33 +134,26 @@ A2E7E45B17F037F4008F045A /* IDEFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E7E45A17F037F4008F045A /* IDEFoundation.framework */; }; A2E7E45D17F037FF008F045A /* DVTKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E7E45C17F037FF008F045A /* DVTKit.framework */; }; A2E7E45F17F03802008F045A /* DVTFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E7E45E17F03802008F045A /* DVTFoundation.framework */; }; - A2E7E46117F0380F008F045A /* IDESourceEditor.ideplugin in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E7E46017F0380F008F045A /* IDESourceEditor.ideplugin */; }; - A2E7E46217F03828008F045A /* IDESourceEditor.ideplugin in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E7E46017F0380F008F045A /* IDESourceEditor.ideplugin */; }; A2E7E46317F0382E008F045A /* IDEFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E7E45A17F037F4008F045A /* IDEFoundation.framework */; }; A2E7E46417F0382E008F045A /* IDEKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E7E45817F037EF008F045A /* IDEKit.framework */; }; A2E7E46517F03835008F045A /* DVTFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E7E45E17F03802008F045A /* DVTFoundation.framework */; }; A2E7E46617F03835008F045A /* DVTKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2E7E45C17F037FF008F045A /* DVTKit.framework */; }; - A2F2621B182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m */; }; - A2F2621C182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m */; }; + A2F2621B182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.m */; }; + A2F2621C182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.m */; }; A2F4A33E14F00B25006DA5A5 /* XVimNormalEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F4A33D14F00B25006DA5A5 /* XVimNormalEvaluator.m */; }; A2F4A34314F00BD3006DA5A5 /* XVimVisualEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F4A34214F00BD3006DA5A5 /* XVimVisualEvaluator.m */; }; A2FF17D11502B292003FE648 /* XVimMotionEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = A2FF17D01502B291003FE648 /* XVimMotionEvaluator.m */; }; BF07A94215EBF404006E6984 /* NSEvent+VimHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = BF050D7A15EBF3E100CD0D12 /* NSEvent+VimHelper.m */; }; C308865A1554C8380024161D /* NSInsetTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = C30886591554C8380024161D /* NSInsetTextView.m */; }; C324BE0115390E8500C13558 /* XVimGVisualEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C324BE0015390E8500C13558 /* XVimGVisualEvaluator.m */; }; - C345DDBB154CE12A009F232E /* XVimHookManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C345DDBA154CE12A009F232E /* XVimHookManager.m */; }; C3552D9B153948D200D57577 /* XVimCommandLineEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3552D9A153948D200D57577 /* XVimCommandLineEvaluator.m */; }; C3552DA8153AC6F800D57577 /* XVimHistoryHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = C3552DA7153AC6F800D57577 /* XVimHistoryHandler.m */; }; C361DE681538F18400037B4B /* XVimGActionEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C361DE671538F18400037B4B /* XVimGActionEvaluator.m */; }; C366C23B15287EE30008C58E /* XVimKeymap.m in Sources */ = {isa = PBXBuildFile; fileRef = C366C23A15287EE30008C58E /* XVimKeymap.m */; }; C36C104E153131C600CE1D62 /* XVimTextObjectEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C36C104D153131C500CE1D62 /* XVimTextObjectEvaluator.m */; }; C38A5B301526C6B100E1448D /* XVimKeyStroke.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B2F1526C6B100E1448D /* XVimKeyStroke.m */; }; - C38A5B4115272B0500E1448D /* DVTSourceTextViewHook.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B4015272B0500E1448D /* DVTSourceTextViewHook.m */; }; - C38A5B4715272DCD00E1448D /* IDESourceCodeEditorHook.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B4615272DCD00E1448D /* IDESourceCodeEditorHook.m */; }; C38A5B4D1527CEA500E1448D /* XVimNumericEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C38A5B4C1527CEA400E1448D /* XVimNumericEvaluator.m */; }; - C3AA7226152F186A00C61D97 /* XVimTildeEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AA7225152F186A00C61D97 /* XVimTildeEvaluator.m */; }; - C3AA722A152F1B1800C61D97 /* XVimLowercaseEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AA7229152F1B1700C61D97 /* XVimLowercaseEvaluator.m */; }; - C3AA722D152F1B2800C61D97 /* XVimUppercaseEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AA722C152F1B2700C61D97 /* XVimUppercaseEvaluator.m */; }; + C3AA722A152F1B1800C61D97 /* XVimSwapCharsEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AA7229152F1B1700C61D97 /* XVimSwapCharsEvaluator.m */; }; C3E8F115154267BA007410ED /* XVimMarkSetEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E8F114154267BA007410ED /* XVimMarkSetEvaluator.m */; }; C3E8F11815427AE6007410ED /* XVimArgumentEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E8F11715427AE6007410ED /* XVimArgumentEvaluator.m */; }; C3FA1A1D1532648700059BF6 /* XVimWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = C3FA1A1C1532648700059BF6 /* XVimWindow.m */; }; @@ -209,7 +195,15 @@ 6E2B332E1836E47500EFE4E2 /* XVimTextStoring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimTextStoring.h; path = XVim/XVimTextStoring.h; sourceTree = SOURCE_ROOT; }; 6E2B33321836E60600EFE4E2 /* DVTTextStorage+XVimTextStoring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "DVTTextStorage+XVimTextStoring.h"; path = "XVim/DVTTextStorage+XVimTextStoring.h"; sourceTree = SOURCE_ROOT; }; 6E2B33331836E60600EFE4E2 /* DVTTextStorage+XVimTextStoring.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "DVTTextStorage+XVimTextStoring.m"; path = "XVim/DVTTextStorage+XVimTextStoring.m"; sourceTree = SOURCE_ROOT; }; + 6E2C392D183E4BA20056E30F /* XVimView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimView.h; path = XVim/XVimView.h; sourceTree = SOURCE_ROOT; }; + 6E2C392E183E4BA20056E30F /* XVimView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimView.m; path = XVim/XVimView.m; sourceTree = SOURCE_ROOT; }; + 6E6865D418390702008D5FBB /* XVimUndo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimUndo.h; path = XVim/XVimUndo.h; sourceTree = SOURCE_ROOT; }; + 6E6865D518390702008D5FBB /* XVimUndo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimUndo.m; path = XVim/XVimUndo.m; sourceTree = SOURCE_ROOT; }; 6EAFF976183777A6003EADAE /* XVimStringBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimStringBuffer.h; path = XVim/XVimStringBuffer.h; sourceTree = SOURCE_ROOT; }; + 6EC4A31C1843A4F5003E2BB5 /* NSCharacterSet+XVimAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSCharacterSet+XVimAdditions.h"; path = "XVim/NSCharacterSet+XVimAdditions.h"; sourceTree = SOURCE_ROOT; }; + 6EC4A31D1843A4F5003E2BB5 /* NSCharacterSet+XVimAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSCharacterSet+XVimAdditions.m"; path = "XVim/NSCharacterSet+XVimAdditions.m"; sourceTree = SOURCE_ROOT; }; + 6EF220D4183E2A1400B814B8 /* NSObject+XVimAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSObject+XVimAdditions.h"; path = "XVim/NSObject+XVimAdditions.h"; sourceTree = SOURCE_ROOT; }; + 6EF220D5183E2A1400B814B8 /* NSObject+XVimAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSObject+XVimAdditions.m"; path = "XVim/NSObject+XVimAdditions.m"; sourceTree = SOURCE_ROOT; }; A2126B7316FB30B0000BE21C /* XVimMark.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimMark.h; path = XVim/XVimMark.h; sourceTree = SOURCE_ROOT; }; A2126B7416FB30B0000BE21C /* XVimMark.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimMark.m; path = XVim/XVimMark.m; sourceTree = SOURCE_ROOT; }; A2126B7616FB4806000BE21C /* XVimMarks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimMarks.h; path = XVim/XVimMarks.h; sourceTree = SOURCE_ROOT; }; @@ -261,18 +255,12 @@ A257540B1732A58E003D8A97 /* XVimTester+Visual.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "XVimTester+Visual.m"; path = "XVim/Test/XVimTester+Visual.m"; sourceTree = SOURCE_ROOT; }; A257C28D156567250098CA09 /* DVTSourceTextView+XVim.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "DVTSourceTextView+XVim.h"; path = "XVim/DVTSourceTextView+XVim.h"; sourceTree = SOURCE_ROOT; }; A257C28E156567250098CA09 /* DVTSourceTextView+XVim.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "DVTSourceTextView+XVim.m"; path = "XVim/DVTSourceTextView+XVim.m"; sourceTree = SOURCE_ROOT; }; - A2640CC717ACE651003D197D /* NSTextView+VimOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSTextView+VimOperation.h"; path = "XVim/NSTextView+VimOperation.h"; sourceTree = SOURCE_ROOT; }; - A2640CC817ACE651003D197D /* NSTextView+VimOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSTextView+VimOperation.m"; path = "XVim/NSTextView+VimOperation.m"; sourceTree = SOURCE_ROOT; }; - A26ACC4C154F2D6600B27D69 /* IDEEditorHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IDEEditorHook.h; path = XVim/IDEEditorHook.h; sourceTree = SOURCE_ROOT; }; - A26ACC4D154F2D6700B27D69 /* IDEEditorHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IDEEditorHook.m; path = XVim/IDEEditorHook.m; sourceTree = SOURCE_ROOT; }; + A26ACC4C154F2D6600B27D69 /* IDEEditor+XVim.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "IDEEditor+XVim.h"; path = "XVim/IDEEditor+XVim.h"; sourceTree = SOURCE_ROOT; }; + A26ACC4D154F2D6700B27D69 /* IDEEditor+XVim.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "IDEEditor+XVim.m"; path = "XVim/IDEEditor+XVim.m"; sourceTree = SOURCE_ROOT; }; A26DC88415DF33C600779CB4 /* XVimTester.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimTester.h; path = XVim/XVimTester.h; sourceTree = SOURCE_ROOT; }; A26DC88515DF33C600779CB4 /* XVimTester.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimTester.m; path = XVim/XVimTester.m; sourceTree = SOURCE_ROOT; }; A26E5F6217A6BCAF0087C9B6 /* NSTextStorage+VimOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSTextStorage+VimOperation.h"; path = "XVim/NSTextStorage+VimOperation.h"; sourceTree = SOURCE_ROOT; }; A26E5F6317A6BCAF0087C9B6 /* NSTextStorage+VimOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSTextStorage+VimOperation.m"; path = "XVim/NSTextStorage+VimOperation.m"; sourceTree = SOURCE_ROOT; }; - A2702B0016FE537D005ADD76 /* IDEWorkspaceWindowHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IDEWorkspaceWindowHook.h; path = XVim/IDEWorkspaceWindowHook.h; sourceTree = SOURCE_ROOT; }; - A2702B0116FE537D005ADD76 /* IDEWorkspaceWindowHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IDEWorkspaceWindowHook.m; path = XVim/IDEWorkspaceWindowHook.m; sourceTree = SOURCE_ROOT; }; - A2702B0916FEA313005ADD76 /* NSObject+ExtraData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSObject+ExtraData.h"; path = "XVim/NSObject+ExtraData.h"; sourceTree = ""; }; - A2702B0A16FEA313005ADD76 /* NSObject+ExtraData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSObject+ExtraData.m"; path = "XVim/NSObject+ExtraData.m"; sourceTree = ""; }; A2771E6A179EF520003B621E /* XVimTester+Issues.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "XVimTester+Issues.m"; path = "XVim/Test/XVimTester+Issues.m"; sourceTree = SOURCE_ROOT; }; A28D42F314FE87AF004BC121 /* XVimGMotionEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimGMotionEvaluator.h; path = XVim/XVimGMotionEvaluator.h; sourceTree = SOURCE_ROOT; }; A28D42F414FE87AF004BC121 /* XVimGMotionEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimGMotionEvaluator.m; path = XVim/XVimGMotionEvaluator.m; sourceTree = SOURCE_ROOT; }; @@ -281,7 +269,6 @@ A28D895617BFF434002709D8 /* XVimTester+Search.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "XVimTester+Search.m"; path = "XVim/Test/XVimTester+Search.m"; sourceTree = SOURCE_ROOT; }; A293A38616CE8A8000E1E827 /* XVimDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimDebug.h; path = XVim/XVimDebug.h; sourceTree = SOURCE_ROOT; }; A293A38716CE8A8000E1E827 /* XVimDebug.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimDebug.m; path = XVim/XVimDebug.m; sourceTree = SOURCE_ROOT; }; - A2A193FF1608E40000809FBE /* XVimTextViewProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = XVimTextViewProtocol.h; path = XVim/XVimTextViewProtocol.h; sourceTree = SOURCE_ROOT; }; A2A6BA2C152A544B00F0EB5F /* XVimSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimSearch.h; path = XVim/XVimSearch.h; sourceTree = SOURCE_ROOT; }; A2A6BA2D152A544B00F0EB5F /* XVimSearch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimSearch.m; path = XVim/XVimSearch.m; sourceTree = SOURCE_ROOT; }; A2A736341527484B0051E8E4 /* XVimExCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimExCommand.h; path = XVim/XVimExCommand.h; sourceTree = SOURCE_ROOT; }; @@ -290,8 +277,6 @@ A2A8A4FF14E41C66002EA6C8 /* XVimCommandLine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimCommandLine.m; path = XVim/XVimCommandLine.m; sourceTree = SOURCE_ROOT; }; A2ABFE1115497A3C002220E8 /* XVimStatusLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimStatusLine.h; path = XVim/XVimStatusLine.h; sourceTree = SOURCE_ROOT; }; A2ABFE1215497A3C002220E8 /* XVimStatusLine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimStatusLine.m; path = XVim/XVimStatusLine.m; sourceTree = SOURCE_ROOT; }; - A2ADAB811545C18F0093A908 /* IDEEditorAreaHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IDEEditorAreaHook.h; path = XVim/IDEEditorAreaHook.h; sourceTree = SOURCE_ROOT; }; - A2ADAB821545C18F0093A908 /* IDEEditorAreaHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IDEEditorAreaHook.m; path = XVim/IDEEditorAreaHook.m; sourceTree = SOURCE_ROOT; }; A2AFD7321790F2FD009B442B /* XVimRecordingEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimRecordingEvaluator.h; path = XVim/XVimRecordingEvaluator.h; sourceTree = SOURCE_ROOT; }; A2AFD7331790F2FF009B442B /* XVimRecordingEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimRecordingEvaluator.m; path = XVim/XVimRecordingEvaluator.m; sourceTree = SOURCE_ROOT; }; A2AFD73617914ACE009B442B /* XVimTester+Recording.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "XVimTester+Recording.m"; path = "XVim/XVimTester+Recording.m"; sourceTree = SOURCE_ROOT; }; @@ -303,8 +288,6 @@ A2BA3EA1152E372A00C18FB4 /* XVimOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimOptions.m; path = XVim/XVimOptions.m; sourceTree = SOURCE_ROOT; }; A2C1BB5116CEAC7F0066F420 /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Utils.h; path = XVim/Utils.h; sourceTree = ""; }; A2C1BB5216CEAC7F0066F420 /* Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Utils.m; path = XVim/Utils.m; sourceTree = ""; }; - A2C4E79614E7F35300751199 /* Hooker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Hooker.h; path = XVim/Hooker.h; sourceTree = ""; }; - A2C4E79714E7F35300751199 /* Hooker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Hooker.m; path = XVim/Hooker.m; sourceTree = ""; }; A2C4E79A14E7F36B00751199 /* XVimEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimEvaluator.h; path = XVim/XVimEvaluator.h; sourceTree = SOURCE_ROOT; }; A2C4E79B14E7F36B00751199 /* XVimEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; name = XVimEvaluator.m; path = XVim/XVimEvaluator.m; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; A2C55FC917EE9F49000EBEA7 /* DVTFoundation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DVTFoundation.h; path = XcodeClasses/Xcode4/DVTFoundation.h; sourceTree = SOURCE_ROOT; }; @@ -317,13 +300,13 @@ A2D5ADFF17A0172F00430761 /* UserDefaultsList.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = UserDefaultsList.txt; sourceTree = ""; }; A2E7E45217EF0219008F045A /* XVim.xcplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XVim.xcplugin; sourceTree = BUILT_PRODUCTS_DIR; }; A2E7E45517EFF677008F045A /* Info_Xcode5.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info_Xcode5.plist; path = XVim/Info_Xcode5.plist; sourceTree = SOURCE_ROOT; }; - A2E7E45817F037EF008F045A /* IDEKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IDEKit.framework; path = "../../../../Applications/Xcode 2.app/Contents/Frameworks/IDEKit.framework"; sourceTree = ""; }; - A2E7E45A17F037F4008F045A /* IDEFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IDEFoundation.framework; path = "../../../../Applications/Xcode 2.app/Contents/Frameworks/IDEFoundation.framework"; sourceTree = ""; }; - A2E7E45C17F037FF008F045A /* DVTKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DVTKit.framework; path = "../../../../Applications/Xcode 2.app/Contents/SharedFrameworks/DVTKit.framework"; sourceTree = ""; }; - A2E7E45E17F03802008F045A /* DVTFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DVTFoundation.framework; path = "../../../../Applications/Xcode 2.app/Contents/SharedFrameworks/DVTFoundation.framework"; sourceTree = ""; }; - A2E7E46017F0380F008F045A /* IDESourceEditor.ideplugin */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = IDESourceEditor.ideplugin; path = "../../../../Applications/Xcode 2.app/Contents/PlugIns/IDESourceEditor.ideplugin"; sourceTree = ""; }; - A2F26219182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DVTSourceTextScrollViewHook.h; path = XVim/DVTSourceTextScrollViewHook.h; sourceTree = SOURCE_ROOT; }; - A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DVTSourceTextScrollViewHook.m; path = XVim/DVTSourceTextScrollViewHook.m; sourceTree = SOURCE_ROOT; }; + A2E7E45817F037EF008F045A /* IDEKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IDEKit.framework; path = ../Frameworks/IDEKit.framework; sourceTree = DEVELOPER_DIR; }; + A2E7E45A17F037F4008F045A /* IDEFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IDEFoundation.framework; path = ../Frameworks/IDEFoundation.framework; sourceTree = DEVELOPER_DIR; }; + A2E7E45C17F037FF008F045A /* DVTKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DVTKit.framework; path = ../SharedFrameworks/DVTKit.framework; sourceTree = DEVELOPER_DIR; }; + A2E7E45E17F03802008F045A /* DVTFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DVTFoundation.framework; path = ../SharedFrameworks/DVTFoundation.framework; sourceTree = DEVELOPER_DIR; }; + A2E7E46017F0380F008F045A /* IDESourceEditor.ideplugin */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = IDESourceEditor.ideplugin; path = ../PlugIns/IDESourceEditor.ideplugin; sourceTree = DEVELOPER_DIR; }; + A2F26219182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "DVTSourceTextScrollView+XVim.h"; path = "XVim/DVTSourceTextScrollView+XVim.h"; sourceTree = SOURCE_ROOT; }; + A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "DVTSourceTextScrollView+XVim.m"; path = "XVim/DVTSourceTextScrollView+XVim.m"; sourceTree = SOURCE_ROOT; }; A2F4A33C14F00B25006DA5A5 /* XVimNormalEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimNormalEvaluator.h; path = XVim/XVimNormalEvaluator.h; sourceTree = SOURCE_ROOT; }; A2F4A33D14F00B25006DA5A5 /* XVimNormalEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; name = XVimNormalEvaluator.m; path = XVim/XVimNormalEvaluator.m; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; A2F4A34114F00BD3006DA5A5 /* XVimVisualEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimVisualEvaluator.h; path = XVim/XVimVisualEvaluator.h; sourceTree = SOURCE_ROOT; }; @@ -337,34 +320,22 @@ C324BDFF15390E8500C13558 /* XVimGVisualEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimGVisualEvaluator.h; path = XVim/XVimGVisualEvaluator.h; sourceTree = SOURCE_ROOT; }; C324BE0015390E8500C13558 /* XVimGVisualEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimGVisualEvaluator.m; path = XVim/XVimGVisualEvaluator.m; sourceTree = SOURCE_ROOT; }; C32CE8621532F0E5002BCE2B /* XVimKeymapProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = XVimKeymapProvider.h; path = XVim/XVimKeymapProvider.h; sourceTree = SOURCE_ROOT; }; - C345DDB9154CE12A009F232E /* XVimHookManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimHookManager.h; path = XVim/XVimHookManager.h; sourceTree = SOURCE_ROOT; }; - C345DDBA154CE12A009F232E /* XVimHookManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimHookManager.m; path = XVim/XVimHookManager.m; sourceTree = SOURCE_ROOT; }; C3552D99153948D200D57577 /* XVimCommandLineEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimCommandLineEvaluator.h; path = XVim/XVimCommandLineEvaluator.h; sourceTree = SOURCE_ROOT; }; C3552D9A153948D200D57577 /* XVimCommandLineEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimCommandLineEvaluator.m; path = XVim/XVimCommandLineEvaluator.m; sourceTree = SOURCE_ROOT; }; C3552DA6153AC6F800D57577 /* XVimHistoryHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimHistoryHandler.h; path = XVim/XVimHistoryHandler.h; sourceTree = SOURCE_ROOT; }; C3552DA7153AC6F800D57577 /* XVimHistoryHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimHistoryHandler.m; path = XVim/XVimHistoryHandler.m; sourceTree = SOURCE_ROOT; }; C361DE661538F18400037B4B /* XVimGActionEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimGActionEvaluator.h; path = XVim/XVimGActionEvaluator.h; sourceTree = SOURCE_ROOT; }; C361DE671538F18400037B4B /* XVimGActionEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimGActionEvaluator.m; path = XVim/XVimGActionEvaluator.m; sourceTree = SOURCE_ROOT; }; - C36266CB153455C9000C79D8 /* XVimMotionOption.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = XVimMotionOption.h; path = XVim/XVimMotionOption.h; sourceTree = SOURCE_ROOT; }; C366C23915287EE30008C58E /* XVimKeymap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimKeymap.h; path = XVim/XVimKeymap.h; sourceTree = SOURCE_ROOT; }; C366C23A15287EE30008C58E /* XVimKeymap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimKeymap.m; path = XVim/XVimKeymap.m; sourceTree = SOURCE_ROOT; }; - C36C1045153104EC00CE1D62 /* XVimMotionType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = XVimMotionType.h; path = XVim/XVimMotionType.h; sourceTree = SOURCE_ROOT; }; C36C104C153131C500CE1D62 /* XVimTextObjectEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimTextObjectEvaluator.h; path = XVim/XVimTextObjectEvaluator.h; sourceTree = SOURCE_ROOT; }; C36C104D153131C500CE1D62 /* XVimTextObjectEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimTextObjectEvaluator.m; path = XVim/XVimTextObjectEvaluator.m; sourceTree = SOURCE_ROOT; }; C38A5B2E1526C6B100E1448D /* XVimKeyStroke.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimKeyStroke.h; path = XVim/XVimKeyStroke.h; sourceTree = SOURCE_ROOT; }; C38A5B2F1526C6B100E1448D /* XVimKeyStroke.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimKeyStroke.m; path = XVim/XVimKeyStroke.m; sourceTree = SOURCE_ROOT; }; - C38A5B3F15272B0500E1448D /* DVTSourceTextViewHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DVTSourceTextViewHook.h; path = XVim/DVTSourceTextViewHook.h; sourceTree = SOURCE_ROOT; }; - C38A5B4015272B0500E1448D /* DVTSourceTextViewHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DVTSourceTextViewHook.m; path = XVim/DVTSourceTextViewHook.m; sourceTree = SOURCE_ROOT; }; - C38A5B4515272DCC00E1448D /* IDESourceCodeEditorHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IDESourceCodeEditorHook.h; path = XVim/IDESourceCodeEditorHook.h; sourceTree = SOURCE_ROOT; }; - C38A5B4615272DCD00E1448D /* IDESourceCodeEditorHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IDESourceCodeEditorHook.m; path = XVim/IDESourceCodeEditorHook.m; sourceTree = SOURCE_ROOT; }; C38A5B4B1527CEA400E1448D /* XVimNumericEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimNumericEvaluator.h; path = XVim/XVimNumericEvaluator.h; sourceTree = SOURCE_ROOT; }; C38A5B4C1527CEA400E1448D /* XVimNumericEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimNumericEvaluator.m; path = XVim/XVimNumericEvaluator.m; sourceTree = SOURCE_ROOT; }; - C3AA7224152F186A00C61D97 /* XVimTildeEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimTildeEvaluator.h; path = XVim/XVimTildeEvaluator.h; sourceTree = SOURCE_ROOT; }; - C3AA7225152F186A00C61D97 /* XVimTildeEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimTildeEvaluator.m; path = XVim/XVimTildeEvaluator.m; sourceTree = SOURCE_ROOT; }; - C3AA7228152F1B1700C61D97 /* XVimLowercaseEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimLowercaseEvaluator.h; path = XVim/XVimLowercaseEvaluator.h; sourceTree = SOURCE_ROOT; }; - C3AA7229152F1B1700C61D97 /* XVimLowercaseEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimLowercaseEvaluator.m; path = XVim/XVimLowercaseEvaluator.m; sourceTree = SOURCE_ROOT; }; - C3AA722B152F1B2700C61D97 /* XVimUppercaseEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimUppercaseEvaluator.h; path = XVim/XVimUppercaseEvaluator.h; sourceTree = SOURCE_ROOT; }; - C3AA722C152F1B2700C61D97 /* XVimUppercaseEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimUppercaseEvaluator.m; path = XVim/XVimUppercaseEvaluator.m; sourceTree = SOURCE_ROOT; }; + C3AA7228152F1B1700C61D97 /* XVimSwapCharsEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimSwapCharsEvaluator.h; path = XVim/XVimSwapCharsEvaluator.h; sourceTree = SOURCE_ROOT; }; + C3AA7229152F1B1700C61D97 /* XVimSwapCharsEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimSwapCharsEvaluator.m; path = XVim/XVimSwapCharsEvaluator.m; sourceTree = SOURCE_ROOT; }; C3E8F113154267BA007410ED /* XVimMarkSetEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimMarkSetEvaluator.h; path = XVim/XVimMarkSetEvaluator.h; sourceTree = SOURCE_ROOT; }; C3E8F114154267BA007410ED /* XVimMarkSetEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XVimMarkSetEvaluator.m; path = XVim/XVimMarkSetEvaluator.m; sourceTree = SOURCE_ROOT; }; C3E8F11615427AE6007410ED /* XVimArgumentEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XVimArgumentEvaluator.h; path = XVim/XVimArgumentEvaluator.h; sourceTree = SOURCE_ROOT; }; @@ -390,7 +361,6 @@ files = ( A28F427217EEDBC200A3F7AE /* AppKit.framework in Frameworks */, A28F427317EEDBC200A3F7AE /* Cocoa.framework in Frameworks */, - A2E7E46117F0380F008F045A /* IDESourceEditor.ideplugin in Frameworks */, A2E7E45D17F037FF008F045A /* DVTKit.framework in Frameworks */, A2E7E45F17F03802008F045A /* DVTFoundation.framework in Frameworks */, A2E7E45917F037EF008F045A /* IDEKit.framework in Frameworks */, @@ -408,7 +378,6 @@ A2E7E46617F03835008F045A /* DVTKit.framework in Frameworks */, A2E7E46317F0382E008F045A /* IDEFoundation.framework in Frameworks */, A2E7E46417F0382E008F045A /* IDEKit.framework in Frameworks */, - A2E7E46217F03828008F045A /* IDESourceEditor.ideplugin in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -421,12 +390,36 @@ 6E2B332E1836E47500EFE4E2 /* XVimTextStoring.h */, A26E5F6217A6BCAF0087C9B6 /* NSTextStorage+VimOperation.h */, A26E5F6317A6BCAF0087C9B6 /* NSTextStorage+VimOperation.m */, + 6E6865D418390702008D5FBB /* XVimUndo.h */, + 6E6865D518390702008D5FBB /* XVimUndo.m */, 6E2B332A1836E42F00EFE4E2 /* XVimBuffer.h */, 6E2B332B1836E42F00EFE4E2 /* XVimBuffer.m */, ); name = Buffer; sourceTree = ""; }; + 6E2C392C183E4B880056E30F /* View */ = { + isa = PBXGroup; + children = ( + A23771571611CD4200FE5F8B /* XVimMotion.h */, + A23771581611CD4200FE5F8B /* XVimMotion.m */, + 6E2C392D183E4BA20056E30F /* XVimView.h */, + 6E2C392E183E4BA20056E30F /* XVimView.m */, + ); + name = View; + sourceTree = ""; + }; + 6EF220D3183E29EC00B814B8 /* Foundation */ = { + isa = PBXGroup; + children = ( + 6EF220D4183E2A1400B814B8 /* NSObject+XVimAdditions.h */, + 6EF220D5183E2A1400B814B8 /* NSObject+XVimAdditions.m */, + 6EC4A31C1843A4F5003E2BB5 /* NSCharacterSet+XVimAdditions.h */, + 6EC4A31D1843A4F5003E2BB5 /* NSCharacterSet+XVimAdditions.m */, + ); + name = Foundation; + sourceTree = ""; + }; A222B5E31514DFD5005E8802 /* Operators */ = { isa = PBXGroup; children = ( @@ -440,12 +433,8 @@ A2277EDA14F80D5000A6B70C /* XVimYankEvaluator.m */, A2277ED214F80BCB00A6B70C /* XVimDeleteEvaluator.h */, A2277ED314F80BCB00A6B70C /* XVimDeleteEvaluator.m */, - C3AA7224152F186A00C61D97 /* XVimTildeEvaluator.h */, - C3AA7225152F186A00C61D97 /* XVimTildeEvaluator.m */, - C3AA7228152F1B1700C61D97 /* XVimLowercaseEvaluator.h */, - C3AA7229152F1B1700C61D97 /* XVimLowercaseEvaluator.m */, - C3AA722B152F1B2700C61D97 /* XVimUppercaseEvaluator.h */, - C3AA722C152F1B2700C61D97 /* XVimUppercaseEvaluator.m */, + C3AA7228152F1B1700C61D97 /* XVimSwapCharsEvaluator.h */, + C3AA7229152F1B1700C61D97 /* XVimSwapCharsEvaluator.m */, C36C104C153131C500CE1D62 /* XVimTextObjectEvaluator.h */, C36C104D153131C500CE1D62 /* XVimTextObjectEvaluator.m */, A23D002217DA0C7D007EBB21 /* XVimJoinEvaluator.h */, @@ -514,6 +503,7 @@ A223956116653ACD00CD26AE /* Documents */, A2C4E78F14E7EA0B00751199 /* Support */, A2F41B0F14D70BD000A0314F /* Debug */, + C38A5B481527C9EE00E1448D /* Xcode Class Definitions */, A2B4BAC114D59F6600D817B0 /* XVim */, A2B4BABA14D59F6600D817B0 /* Frameworks */, A234821814E7E26A003E0FF3 /* XVim.xcplugin */, @@ -548,19 +538,17 @@ A2B4BAC114D59F6600D817B0 /* XVim */ = { isa = PBXGroup; children = ( + 6EF220D3183E29EC00B814B8 /* Foundation */, A2165BFC17A3762C00AB18FD /* XVimDefs.h */, A24782B714D6F56E003B6433 /* XVim.h */, A24782B814D6F56E003B6433 /* XVim.m */, A22A009E1708914D0003046C /* XVimUtil.h */, A22A009F1708914D0003046C /* XVimUtil.m */, - C345DDB8154CCF79009F232E /* Event Dispatchers */, A2F4A34014F00B35006DA5A5 /* Event Handlers(Evaluators) */, C345DDB7154CCF1F009F232E /* Xcode Dependent Classes */, C345DDB4154CCE5E009F232E /* XCode Independent Classes */, A26DC88315DF339A00779CB4 /* Testing */, - C38A5B481527C9EE00E1448D /* Xcode Class Definitions */, A2B4BAC214D59F6600D817B0 /* Supporting Files */, - A2A193FF1608E40000809FBE /* XVimTextViewProtocol.h */, ); name = XVim; path = XVim_lite2; @@ -580,12 +568,8 @@ A2C4E78F14E7EA0B00751199 /* Support */ = { isa = PBXGroup; children = ( - A2C4E79614E7F35300751199 /* Hooker.h */, - A2C4E79714E7F35300751199 /* Hooker.m */, A2C1BB5116CEAC7F0066F420 /* Utils.h */, A2C1BB5216CEAC7F0066F420 /* Utils.m */, - A2702B0916FEA313005ADD76 /* NSObject+ExtraData.h */, - A2702B0A16FEA313005ADD76 /* NSObject+ExtraData.m */, ); name = Support; sourceTree = ""; @@ -679,11 +663,10 @@ children = ( C345DDB6154CCE7E009F232E /* Strings */, 6E08A8781836DC9F00905508 /* Buffer */, + 6E2C392C183E4B880056E30F /* View */, C3552DA6153AC6F800D57577 /* XVimHistoryHandler.h */, C3552DA7153AC6F800D57577 /* XVimHistoryHandler.m */, - C36C1045153104EC00CE1D62 /* XVimMotionType.h */, C32CE8621532F0E5002BCE2B /* XVimKeymapProvider.h */, - C36266CB153455C9000C79D8 /* XVimMotionOption.h */, F17D0139150861DC00A8111B /* XVimRegister.h */, F17D013A150861DC00A8111B /* XVimRegister.m */, C38A5B2E1526C6B100E1448D /* XVimKeyStroke.h */, @@ -698,14 +681,10 @@ A293A38716CE8A8000E1E827 /* XVimDebug.m */, A2A6BA2C152A544B00F0EB5F /* XVimSearch.h */, A2A6BA2D152A544B00F0EB5F /* XVimSearch.m */, - A23771571611CD4200FE5F8B /* XVimMotion.h */, - A23771581611CD4200FE5F8B /* XVimMotion.m */, A2126B7316FB30B0000BE21C /* XVimMark.h */, A2126B7416FB30B0000BE21C /* XVimMark.m */, A2126B7616FB4806000BE21C /* XVimMarks.h */, A2126B7716FB4806000BE21C /* XVimMarks.m */, - A2640CC717ACE651003D197D /* NSTextView+VimOperation.h */, - A2640CC817ACE651003D197D /* NSTextView+VimOperation.m */, ); name = "XCode Independent Classes"; sourceTree = ""; @@ -735,8 +714,12 @@ A24782BA14D6F56E003B6433 /* XVimCommandField.m */, A2A8A4FE14E41C66002EA6C8 /* XVimCommandLine.h */, A2A8A4FF14E41C66002EA6C8 /* XVimCommandLine.m */, + A26ACC4C154F2D6600B27D69 /* IDEEditor+XVim.h */, + A26ACC4D154F2D6700B27D69 /* IDEEditor+XVim.m */, A216F39F156560FE00AD2529 /* IDEEditorArea+XVim.h */, A216F3A0156560FE00AD2529 /* IDEEditorArea+XVim.m */, + A2F26219182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.h */, + A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.m */, A257C28D156567250098CA09 /* DVTSourceTextView+XVim.h */, A257C28E156567250098CA09 /* DVTSourceTextView+XVim.m */, 6E2B33321836E60600EFE4E2 /* DVTTextStorage+XVimTextStoring.h */, @@ -745,27 +728,6 @@ name = "Xcode Dependent Classes"; sourceTree = ""; }; - C345DDB8154CCF79009F232E /* Event Dispatchers */ = { - isa = PBXGroup; - children = ( - C38A5B3F15272B0500E1448D /* DVTSourceTextViewHook.h */, - C38A5B4015272B0500E1448D /* DVTSourceTextViewHook.m */, - A2F26219182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.h */, - A2F2621A182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m */, - C38A5B4515272DCC00E1448D /* IDESourceCodeEditorHook.h */, - C38A5B4615272DCD00E1448D /* IDESourceCodeEditorHook.m */, - A2ADAB811545C18F0093A908 /* IDEEditorAreaHook.h */, - A2ADAB821545C18F0093A908 /* IDEEditorAreaHook.m */, - A26ACC4C154F2D6600B27D69 /* IDEEditorHook.h */, - A26ACC4D154F2D6700B27D69 /* IDEEditorHook.m */, - A2702B0016FE537D005ADD76 /* IDEWorkspaceWindowHook.h */, - A2702B0116FE537D005ADD76 /* IDEWorkspaceWindowHook.m */, - C345DDB9154CE12A009F232E /* XVimHookManager.h */, - C345DDBA154CE12A009F232E /* XVimHookManager.m */, - ); - name = "Event Dispatchers"; - sourceTree = ""; - }; C38A5B481527C9EE00E1448D /* Xcode Class Definitions */ = { isa = PBXGroup; children = ( @@ -773,6 +735,7 @@ A2C55FC817EE9F29000EBEA7 /* Xcode4 */, ); name = "Xcode Class Definitions"; + path = XVim_lite2; sourceTree = ""; }; /* End PBXGroup section */ @@ -820,7 +783,7 @@ A2B4BAAF14D59F6600D817B0 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0460; + LastUpgradeCheck = 0500; }; buildConfigurationList = A2B4BAB214D59F6600D817B0 /* Build configuration list for PBXProject "XVim" */; compatibilityVersion = "Xcode 3.2"; @@ -845,7 +808,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - A2243B7017F03BFC00DD92A1 /* Info_Xcode5.plist in Resources */, + 6EE2579E183F81C3006E4E1A /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -868,7 +831,6 @@ A28F422917EEDBC200A3F7AE /* XVim.m in Sources */, A28F422A17EEDBC200A3F7AE /* XVimCommandField.m in Sources */, A28F422B17EEDBC200A3F7AE /* XVimCommandLine.m in Sources */, - A28F422C17EEDBC200A3F7AE /* Hooker.m in Sources */, A28F422D17EEDBC200A3F7AE /* XVimEvaluator.m in Sources */, A28F422E17EEDBC200A3F7AE /* XVimNormalEvaluator.m in Sources */, A28F422F17EEDBC200A3F7AE /* XVimVisualEvaluator.m in Sources */, @@ -881,19 +843,17 @@ A28F423617EEDBC200A3F7AE /* XVimZEvaluator.m in Sources */, A28F423717EEDBC200A3F7AE /* XVimEqualEvaluator.m in Sources */, A28F423817EEDBC200A3F7AE /* XVimRegister.m in Sources */, + 6EC4A31F1843A4F5003E2BB5 /* NSCharacterSet+XVimAdditions.m in Sources */, A28F423917EEDBC200A3F7AE /* XVimRegisterEvaluator.m in Sources */, A28F423A17EEDBC200A3F7AE /* XVimOperatorEvaluator.m in Sources */, A28F423B17EEDBC200A3F7AE /* XVimKeyStroke.m in Sources */, - A28F423C17EEDBC200A3F7AE /* DVTSourceTextViewHook.m in Sources */, - A28F423D17EEDBC200A3F7AE /* IDESourceCodeEditorHook.m in Sources */, A28F423E17EEDBC200A3F7AE /* XVimNumericEvaluator.m in Sources */, A28F423F17EEDBC200A3F7AE /* XVimKeymap.m in Sources */, A28F424017EEDBC200A3F7AE /* XVimExCommand.m in Sources */, + 6EF220D7183E2A1400B814B8 /* NSObject+XVimAdditions.m in Sources */, A28F424117EEDBC200A3F7AE /* XVimSearch.m in Sources */, A28F424217EEDBC200A3F7AE /* XVimOptions.m in Sources */, - A28F424317EEDBC200A3F7AE /* XVimTildeEvaluator.m in Sources */, - A28F424417EEDBC200A3F7AE /* XVimLowercaseEvaluator.m in Sources */, - A28F424517EEDBC200A3F7AE /* XVimUppercaseEvaluator.m in Sources */, + A28F424417EEDBC200A3F7AE /* XVimSwapCharsEvaluator.m in Sources */, A28F424617EEDBC200A3F7AE /* XVimWindowEvaluator.m in Sources */, A28F424717EEDBC200A3F7AE /* XVimTextObjectEvaluator.m in Sources */, A28F424817EEDBC200A3F7AE /* XVimWindow.m in Sources */, @@ -904,11 +864,9 @@ A28F424D17EEDBC200A3F7AE /* XVimHistoryHandler.m in Sources */, A28F424E17EEDBC200A3F7AE /* XVimMarkSetEvaluator.m in Sources */, A28F424F17EEDBC200A3F7AE /* XVimArgumentEvaluator.m in Sources */, - A28F425017EEDBC200A3F7AE /* IDEEditorAreaHook.m in Sources */, A28F425117EEDBC200A3F7AE /* XVimStatusLine.m in Sources */, - A28F425217EEDBC200A3F7AE /* XVimHookManager.m in Sources */, 6E2B332D1836E42F00EFE4E2 /* XVimBuffer.m in Sources */, - A28F425317EEDBC200A3F7AE /* IDEEditorHook.m in Sources */, + A28F425317EEDBC200A3F7AE /* IDEEditor+XVim.m in Sources */, A28F425417EEDBC200A3F7AE /* NSInsetTextView.m in Sources */, A28F425517EEDBC200A3F7AE /* IDEEditorArea+XVim.m in Sources */, A28F425617EEDBC200A3F7AE /* DVTSourceTextView+XVim.m in Sources */, @@ -916,18 +874,18 @@ A28F425817EEDBC200A3F7AE /* NSEvent+VimHelper.m in Sources */, A28F425917EEDBC200A3F7AE /* XVimMotion.m in Sources */, A28F425A17EEDBC200A3F7AE /* XVimDebug.m in Sources */, + 6E6865D718390702008D5FBB /* XVimUndo.m in Sources */, A28F425B17EEDBC200A3F7AE /* Utils.m in Sources */, A28F425C17EEDBC200A3F7AE /* XVimMark.m in Sources */, - A2F2621C182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m in Sources */, + A2F2621C182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.m in Sources */, A28F425D17EEDBC200A3F7AE /* XVimMarks.m in Sources */, - A28F425E17EEDBC200A3F7AE /* IDEWorkspaceWindowHook.m in Sources */, - A28F425F17EEDBC200A3F7AE /* NSObject+ExtraData.m in Sources */, A28F426017EEDBC200A3F7AE /* XVimUtil.m in Sources */, A28F426117EEDBC200A3F7AE /* XVimTestCase.m in Sources */, A28F426217EEDBC200A3F7AE /* XVimTester+TextObject.m in Sources */, A28F426317EEDBC200A3F7AE /* XVimTester+Motion.m in Sources */, A28F426417EEDBC200A3F7AE /* XVimTester+Operator.m in Sources */, A28F426517EEDBC200A3F7AE /* XVimTester+Mark.m in Sources */, + 6E2C3930183E4BA20056E30F /* XVimView.m in Sources */, A28F426617EEDBC200A3F7AE /* XVimTester+Visual.m in Sources */, A28F426717EEDBC200A3F7AE /* XVimTester+map.m in Sources */, 6E2B33341836E60600EFE4E2 /* DVTTextStorage+XVimTextStoring.m in Sources */, @@ -936,7 +894,6 @@ A28F426A17EEDBC200A3F7AE /* XVimTester+Recording.m in Sources */, A28F426B17EEDBC200A3F7AE /* XVimTester+Issues.m in Sources */, A28F426C17EEDBC200A3F7AE /* NSTextStorage+VimOperation.m in Sources */, - A28F426D17EEDBC200A3F7AE /* NSTextView+VimOperation.m in Sources */, A28F426E17EEDBC200A3F7AE /* XVimTester+ExCmd.m in Sources */, A28F426F17EEDBC200A3F7AE /* XVimTester+Search.m in Sources */, A28F427017EEDBC200A3F7AE /* XVimJoinEvaluator.m in Sources */, @@ -951,7 +908,6 @@ A24782C114D6F56E003B6433 /* XVim.m in Sources */, A24782C214D6F56E003B6433 /* XVimCommandField.m in Sources */, A2A8A50014E41C66002EA6C8 /* XVimCommandLine.m in Sources */, - A2C4E79814E7F35300751199 /* Hooker.m in Sources */, A2C4E79C14E7F36B00751199 /* XVimEvaluator.m in Sources */, A2F4A33E14F00B25006DA5A5 /* XVimNormalEvaluator.m in Sources */, A2F4A34314F00BD3006DA5A5 /* XVimVisualEvaluator.m in Sources */, @@ -961,22 +917,19 @@ A28D42F514FE87AF004BC121 /* XVimGMotionEvaluator.m in Sources */, A28D42F814FE8E2A004BC121 /* XVimInsertEvaluator.m in Sources */, A2FF17D11502B292003FE648 /* XVimMotionEvaluator.m in Sources */, + 6EC4A31E1843A4F5003E2BB5 /* NSCharacterSet+XVimAdditions.m in Sources */, F1F2426D15041A9B00F706A4 /* XVimZEvaluator.m in Sources */, F1C1E515150475ED0005C1CB /* XVimEqualEvaluator.m in Sources */, F17D013B150861DC00A8111B /* XVimRegister.m in Sources */, F100DC2E150BB6BC002C703C /* XVimRegisterEvaluator.m in Sources */, A222B5E11514DFCD005E8802 /* XVimOperatorEvaluator.m in Sources */, C38A5B301526C6B100E1448D /* XVimKeyStroke.m in Sources */, - C38A5B4115272B0500E1448D /* DVTSourceTextViewHook.m in Sources */, - C38A5B4715272DCD00E1448D /* IDESourceCodeEditorHook.m in Sources */, C38A5B4D1527CEA500E1448D /* XVimNumericEvaluator.m in Sources */, C366C23B15287EE30008C58E /* XVimKeymap.m in Sources */, A2A736381527484B0051E8E4 /* XVimExCommand.m in Sources */, A2A6BA2E152A544C00F0EB5F /* XVimSearch.m in Sources */, A2BA3EA2152E372A00C18FB4 /* XVimOptions.m in Sources */, - C3AA7226152F186A00C61D97 /* XVimTildeEvaluator.m in Sources */, - C3AA722A152F1B1800C61D97 /* XVimLowercaseEvaluator.m in Sources */, - C3AA722D152F1B2800C61D97 /* XVimUppercaseEvaluator.m in Sources */, + C3AA722A152F1B1800C61D97 /* XVimSwapCharsEvaluator.m in Sources */, F15805C71530B4000031175A /* XVimWindowEvaluator.m in Sources */, C36C104E153131C600CE1D62 /* XVimTextObjectEvaluator.m in Sources */, C3FA1A1D1532648700059BF6 /* XVimWindow.m in Sources */, @@ -987,11 +940,9 @@ C3552DA8153AC6F800D57577 /* XVimHistoryHandler.m in Sources */, C3E8F115154267BA007410ED /* XVimMarkSetEvaluator.m in Sources */, C3E8F11815427AE6007410ED /* XVimArgumentEvaluator.m in Sources */, - A2ADAB831545C18F0093A908 /* IDEEditorAreaHook.m in Sources */, A2ABFE1315497A3C002220E8 /* XVimStatusLine.m in Sources */, - C345DDBB154CE12A009F232E /* XVimHookManager.m in Sources */, 6E2B332C1836E42F00EFE4E2 /* XVimBuffer.m in Sources */, - A26ACC4E154F2D6800B27D69 /* IDEEditorHook.m in Sources */, + A26ACC4E154F2D6800B27D69 /* IDEEditor+XVim.m in Sources */, C308865A1554C8380024161D /* NSInsetTextView.m in Sources */, A216F3A1156560FE00AD2529 /* IDEEditorArea+XVim.m in Sources */, A257C28F156567250098CA09 /* DVTSourceTextView+XVim.m in Sources */, @@ -1001,10 +952,9 @@ A293A38816CE8A8000E1E827 /* XVimDebug.m in Sources */, A2C1BB5316CEAC7F0066F420 /* Utils.m in Sources */, A2126B7516FB30B0000BE21C /* XVimMark.m in Sources */, - A2F2621B182CEC3600AD3DC4 /* DVTSourceTextScrollViewHook.m in Sources */, + A2F2621B182CEC3600AD3DC4 /* DVTSourceTextScrollView+XVim.m in Sources */, A2126B7816FB4806000BE21C /* XVimMarks.m in Sources */, - A2702B0216FE537D005ADD76 /* IDEWorkspaceWindowHook.m in Sources */, - A2702B0B16FEA313005ADD76 /* NSObject+ExtraData.m in Sources */, + 6EF220D6183E2A1400B814B8 /* NSObject+XVimAdditions.m in Sources */, A22A00A01708914E0003046C /* XVimUtil.m in Sources */, A22A00A31709CBE50003046C /* XVimTestCase.m in Sources */, A2575400172F5F0E003D8A97 /* XVimTester+TextObject.m in Sources */, @@ -1016,11 +966,12 @@ A250950D1787B3340090F857 /* XVimTester+Register.m in Sources */, A2AFD7341790F300009B442B /* XVimRecordingEvaluator.m in Sources */, A2AFD73717914ACE009B442B /* XVimTester+Recording.m in Sources */, + 6E6865D618390702008D5FBB /* XVimUndo.m in Sources */, A2771E6B179EF520003B621E /* XVimTester+Issues.m in Sources */, A26E5F6417A6BCAF0087C9B6 /* NSTextStorage+VimOperation.m in Sources */, - A2640CC917ACE651003D197D /* NSTextView+VimOperation.m in Sources */, A24BE47517AD0D31001F797B /* XVimTester+ExCmd.m in Sources */, A28D895717BFF434002709D8 /* XVimTester+Search.m in Sources */, + 6E2C392F183E4BA20056E30F /* XVimView.m in Sources */, A23D002417DA0C7D007EBB21 /* XVimJoinEvaluator.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1044,12 +995,6 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(SYSTEM_APPS_DIR)/Xcode\\ 2.app/Contents/Frameworks", - "$(SYSTEM_APPS_DIR)/Xcode\\ 2.app/Contents/SharedFrameworks", - ); - GCC_ENABLE_OBJC_GC = supported; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "XVim/XVim-Prefix.pch"; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -1071,12 +1016,6 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(SYSTEM_APPS_DIR)/Xcode\\ 2.app/Contents/Frameworks", - "$(SYSTEM_APPS_DIR)/Xcode\\ 2.app/Contents/SharedFrameworks", - ); - GCC_ENABLE_OBJC_GC = supported; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "XVim/XVim-Prefix.pch"; GCC_PREPROCESSOR_DEFINITIONS = "XVIM_XCODE_VERSION=5"; @@ -1095,10 +1034,13 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -1118,9 +1060,11 @@ GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = "$(inherited)"; MACOSX_DEPLOYMENT_TARGET = 10.6; @@ -1135,10 +1079,13 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CONFIGURATION_TEMP_DIR = "$(PROJECT_TEMP_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; COPY_PHASE_STRIP = YES; @@ -1153,9 +1100,11 @@ GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = "$(inherited)"; MACOSX_DEPLOYMENT_TARGET = 10.6; @@ -1169,11 +1118,6 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(SYSTEM_APPS_DIR)/Xcode\\ 2.app/Contents/Frameworks", - "$(SYSTEM_APPS_DIR)/Xcode\\ 2.app/Contents/SharedFrameworks", - ); GCC_ENABLE_OBJC_GC = supported; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "XVim/XVim-Prefix.pch"; @@ -1196,11 +1140,6 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; COMBINE_HIDPI_IMAGES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(SYSTEM_APPS_DIR)/Xcode\\ 2.app/Contents/Frameworks", - "$(SYSTEM_APPS_DIR)/Xcode\\ 2.app/Contents/SharedFrameworks", - ); GCC_ENABLE_OBJC_GC = supported; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "XVim/XVim-Prefix.pch"; diff --git a/XVim.xcodeproj/xcshareddata/xcschemes/XVim for Xcode4.xcscheme b/XVim.xcodeproj/xcshareddata/xcschemes/XVim for Xcode4.xcscheme index e87551b9..15bc299b 100644 --- a/XVim.xcodeproj/xcshareddata/xcschemes/XVim for Xcode4.xcscheme +++ b/XVim.xcodeproj/xcshareddata/xcschemes/XVim for Xcode4.xcscheme @@ -1,6 +1,6 @@ +#import "DVTKit.h" + +@interface DVTSourceTextScrollView (XVim) ++ (void)xvim_initialize; +@end \ No newline at end of file diff --git a/XVim/DVTSourceTextScrollView+XVim.m b/XVim/DVTSourceTextScrollView+XVim.m new file mode 100644 index 00000000..2700b5f1 --- /dev/null +++ b/XVim/DVTSourceTextScrollView+XVim.m @@ -0,0 +1,97 @@ +// +// DVTSourceTextScrollViewHook.m +// XVim +// +// Created by Suzuki Shuichiro on 11/8/13. +// +// + +#import "Logger.h" +#import "XVim.h" +#import "XVimOptions.h" +#import "DVTSourceTextScrollView+XVim.h" +#import "NSObject+XVimAdditions.h" + +@implementation DVTSourceTextScrollView (XVim) + ++ (void)xvim_initialize +{ + if (self == [DVTSourceTextScrollView class]) { +#define swizzle(sel) \ + [self xvim_swizzleInstanceMethod:@selector(sel) with:@selector(xvim_##sel)] + + swizzle(initWithFrame:); + swizzle(dealloc); + swizzle(hasVerticalScroller); + swizzle(hasHorizontalScroller); + swizzle(observeValueForKeyPath:ofObject:change:context:); + +#undef swizzle + } +} + +- (instancetype)xvim_initWithFrame:(NSRect)rect +{ + if ((self = [self xvim_initWithFrame:rect])) { + TRACE_LOG(@"%p initWithFrame", self); + [XVim.instance.options addObserver:self forKeyPath:@"guioptions" + options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_xvim_statusChanged:) + name:XVimEnabledStatusChangedNotification object:nil]; + } + return self; +} + +- (void)xvim_dealloc +{ + @try { + TRACE_LOG(@"%p dealloc", self); + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [XVim.instance.options removeObserver:self forKeyPath:@"guioptions"]; + } + @catch (NSException* exception){ + ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); + [Logger logStackTrace:exception]; + } + [self xvim_dealloc]; +} + +- (void)_xvim_statusChanged:(id)unused +{ + if (XVim.instance.disabled) { + self.hasHorizontalScroller = YES; + self.hasVerticalScroller = YES; + } +} + +- (BOOL)xvim_hasVerticalScroller +{ + if (XVim.instance.disabled) { + return [self xvim_hasVerticalScroller]; + } + return [XVim.instance.options.guioptions rangeOfString:@"r"].location != NSNotFound; +} + +- (BOOL)xvim_hasHorizontalScroller +{ + if (XVim.instance.disabled) { + return [self xvim_hasHorizontalScroller]; + } + return [XVim.instance.options.guioptions rangeOfString:@"b"].location != NSNotFound; +} + +- (void)xvim_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object + change:(NSDictionary *)change context:(void *)context +{ + if (object != XVim.instance.options) { + return [self xvim_observeValueForKeyPath:keyPath ofObject:object + change:change context:context]; + } + if ([keyPath isEqualToString:@"guioptions"]) { + // Just updating the scrollers state. + self.hasHorizontalScroller = self.hasHorizontalScroller; + self.hasVerticalScroller = self.hasVerticalScroller; + } +} + +@end diff --git a/XVim/DVTSourceTextScrollViewHook.h b/XVim/DVTSourceTextScrollViewHook.h deleted file mode 100644 index 317b77af..00000000 --- a/XVim/DVTSourceTextScrollViewHook.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// DVTSourceTextScrollViewHook.h -// XVim -// -// Created by Suzuki Shuichiro on 11/8/13. -// -// - -#import -#import "DVTKit.h" - -@interface DVTSourceTextScrollViewHook : NSObject -+ (void)hook; -+ (void)unhook; -@end - -@interface DVTSourceTextScrollView(Hook) -- (id)initWithFrame_:(NSRect)rect; -- (void)dealloc_; -- (BOOL)hasHorizontalScroller_; -- (BOOL)hasVerticalScroller_; -@end \ No newline at end of file diff --git a/XVim/DVTSourceTextScrollViewHook.m b/XVim/DVTSourceTextScrollViewHook.m deleted file mode 100644 index 495e7f02..00000000 --- a/XVim/DVTSourceTextScrollViewHook.m +++ /dev/null @@ -1,107 +0,0 @@ -// -// DVTSourceTextScrollViewHook.m -// XVim -// -// Created by Suzuki Shuichiro on 11/8/13. -// -// - -#import "Logger.h" -#import "XVim.h" -#import "XVimOptions.h" -#import "Hooker.h" -#import "DVTSourceTextScrollViewHook.h" - - -@implementation DVTSourceTextScrollViewHook -+ (void)hook:(NSString*)method{ - NSString* cls = @"DVTSourceTextScrollView"; - NSString* thisCls = NSStringFromClass([self class]); - [Hooker hookClass:cls method:method byClass:thisCls method:method]; -} - -+ (void)unhook:(NSString*)method{ - NSString* cls = @"DVTSourceTextScrollView"; - [Hooker unhookClass:cls method:method]; -} - -+ (void)hook{ - [self hook:@"initWithFrame:"]; - [self hook:@"dealloc"]; - [self hook:@"hasVerticalScroller"]; - [self hook:@"hasHorizontalScroller"]; - [self hook:@"observeValueForKeyPath:ofObject:change:context:"]; -} - -+ (void)unhook{ - // Never unhook comment outed methods because this class observes XVimOption value on init and remove observe on dealloc. - // Unhooking between init and dealloc leads inconsistent state (and crash) - // [self unhook:@"initWithFrame:"]; - // [self unhook:@"dealloc"]; - [self unhook:@"hasVerticalScroller"]; - [self unhook:@"hasHorizontalScroller"]; - // [self unhook:@"observeValueForKeyPath:ofObject:change:context:"]; -} - -- (id)initWithFrame:(NSRect)rect{ - DVTSourceTextScrollView* base = (DVTSourceTextScrollView*)self; - id obj = [base initWithFrame_:rect]; - if( nil != obj ){ - TRACE_LOG(@"%p initWithFrame", obj); - [XVim.instance.options addObserver:obj forKeyPath:@"guioptions" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - } - return (DVTSourceTextScrollViewHook*)obj; -} - -// This pragma is for suppressing warning that the dealloc method does not call [super dealloc]. ([base dealloc_] calls [super dealloc] so we do not need it) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wall" -- (void)dealloc{ - DVTSourceTextScrollView *base = (DVTSourceTextScrollView*)self; - @try{ - TRACE_LOG(@"%p dealloc", base); - [XVim.instance.options removeObserver:self forKeyPath:@"guioptions"]; - } - @catch (NSException* exception){ - ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); - [Logger logStackTrace:exception]; - } - [base dealloc_]; - return; -} -#pragma GCC diagnostic pop - -- (BOOL)hasVerticalScroller{ - if( [XVim.instance.options.guioptions rangeOfString:@"r"].location == NSNotFound) { - return NO; - }else{ - return YES; - } -} - -- (BOOL)hasHorizontalScroller{ - if( [XVim.instance.options.guioptions rangeOfString:@"b"].location == NSNotFound) { - return NO; - }else{ - return YES; - } -} - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if([keyPath isEqualToString:@"guioptions"]){ - NSScrollView* view = (NSScrollView*)self; - // Just updating the scrollers state. - if( [XVim.instance.options.guioptions rangeOfString:@"r"].location == NSNotFound) { - [view setHasVerticalScroller:NO]; - }else{ - [view setHasVerticalScroller:YES]; - } - if( [XVim.instance.options.guioptions rangeOfString:@"b"].location == NSNotFound) { - [view setHasHorizontalScroller:NO]; - }else{ - [view setHasHorizontalScroller:YES]; - } - } -} - -@end diff --git a/XVim/DVTSourceTextView+XVim.h b/XVim/DVTSourceTextView+XVim.h index 7e400566..c48dbded 100644 --- a/XVim/DVTSourceTextView+XVim.h +++ b/XVim/DVTSourceTextView+XVim.h @@ -13,6 +13,5 @@ #import "DVTKit.h" @interface DVTSourceTextView (XVim) -- (IDEEditorArea*)editorArea; - (XVimWindow*)xvimWindow; @end diff --git a/XVim/DVTSourceTextView+XVim.m b/XVim/DVTSourceTextView+XVim.m index 0eeffcdd..48f982c0 100644 --- a/XVim/DVTSourceTextView+XVim.m +++ b/XVim/DVTSourceTextView+XVim.m @@ -17,6 +17,6 @@ - (IDEEditorArea*)editorArea{ } - (XVimWindow*)xvimWindow{ - return [[self editorArea] xvimWindow]; + return [[self editorArea] xvim_window]; } @end diff --git a/XVim/DVTSourceTextViewHook.h b/XVim/DVTSourceTextViewHook.h deleted file mode 100644 index fea67e71..00000000 --- a/XVim/DVTSourceTextViewHook.h +++ /dev/null @@ -1,43 +0,0 @@ -// -// DVTSourceTextView.h -// XVim -// -// Created by Tomas Lundell on 31/03/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import -#import "DVTKit.h" - -@class DVTSourceTextView; -@class XVimStatusLine; - -@interface DVTSourceTextViewHook : NSObject -+ (void)hook; -+ (void)unhook; -@end - -@interface DVTSourceTextView(Hook) -// When initialize Xcode calls initWithCoder for Xcode4 and initWithFrame:textContainer: for Xcode5 -- (id)initWithCoder_:(NSCoder*)rect; -// - (id)initWithFrame_:(NSRect)rect; // We do not need to hook this -- (id)initWithFrame_:(NSRect)rect textContainer:(NSTextContainer *)container; -- (void)dealloc_; -- (void)setSelectedRanges_:(NSArray*)array affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)flag; -- (void)selectAll_:(id)sender; -- (void)cut_:(id)sender; -- (void)copy_:(id)sender; -- (void)paste_:(id)sender; -- (void)delete_:(id)sender; -- (void)keyDown_:(NSEvent *)theEvent; -- (void)mouseDown_:(NSEvent *)theEvent; -- (void)drawRect_:(NSRect)dirtyRect; -- (BOOL) performKeyEquivalent_:(NSEvent *)theEvent; -- (BOOL)shouldDrawInsertionPoint_; -- (void)_drawInsertionPointInRect_:(NSRect)aRect color:(NSColor*)aColor; -- (void)drawInsertionPointInRect_:(NSRect)aRect color:(NSColor*)aColor turnedOn:(BOOL)flag; -- (BOOL)becomeFirstResponder_; -- (void)didChangeText_; -- (void)viewDidMoveToSuperview_; -- (void)observeValueForKeyPath_:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context; -@end \ No newline at end of file diff --git a/XVim/DVTSourceTextViewHook.m b/XVim/DVTSourceTextViewHook.m deleted file mode 100644 index 4cb48bd5..00000000 --- a/XVim/DVTSourceTextViewHook.m +++ /dev/null @@ -1,318 +0,0 @@ -// -// DVTSourceTextView.m -// XVim -// -// Created by Tomas Lundell on 31/03/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// -#define __XCODE5__ - -#import "DVTFoundation.h" -#import "DVTKit.h" -#import "DVTSourceTextViewHook.h" -#import "XVimEvaluator.h" -#import "XVimWindow.h" -#import "Hooker.h" -#import "Logger.h" -#import "DVTKit.h" -#import "XVimStatusLine.h" -#import "XVim.h" -#import "XVimOptions.h" -#import "IDEKit.h" -#import "IDEEditorArea+XVim.h" -#import "DVTSourceTextView+XVim.h" -#import "NSEvent+VimHelper.h" -#import "NSObject+ExtraData.h" -#import "XVim.h" -#import "XVimUtil.h" -#import "XVimSearch.h" -#import -#import -#import "NSTextView+VimOperation.h" - -@implementation DVTSourceTextViewHook - -+ (void)hook:(NSString*)method{ - NSString* cls = @"DVTSourceTextView"; - NSString* thisCls = NSStringFromClass([self class]); - [Hooker hookClass:cls method:method byClass:thisCls method:method]; -} - -+ (void)unhook:(NSString*)method{ - NSString* cls = @"DVTSourceTextView"; - [Hooker unhookClass:cls method:method]; -} - -+ (void)hook{ - [self hook:@"initWithCoder:"]; - [self hook:@"initWithFrame:textContainer:"]; - [self hook:@"dealloc"]; - [self hook:@"setSelectedRanges:affinity:stillSelecting:"]; - [self hook:@"selectAll:"]; - // [self hook:@"cut:"]; // Cut calls delete: after all. Do not need to hook - // [self hook:@"copy:"]; // Does not change any state. Do not need to hook - [self hook:@"paste:"]; - [self hook:@"delete:"]; - [self hook:@"keyDown:"]; - [self hook:@"mouseDown:"]; - [self hook:@"drawRect:"]; - [self hook:@"_drawInsertionPointInRect:color:"]; - [self hook:@"drawInsertionPointInRect:color:turnedOn:"]; - [self hook:@"didChangeText"]; - [self hook:@"viewDidMoveToSuperview"]; - [self hook:@"shouldChangeTextInRange:replacementString"]; - [self hook:@"observeValueForKeyPath:ofObject:change:context:"]; -} - -+ (void)unhook{ - // We never unhook these two methods. - // This is because we always have to control observers for XVimOptions - // If we unhook these, these may be a memory leak or it leads crash when a removing observer which has not been added as a observer in dealloc method. - // [self unhook:@"initWithCoder:"]; - // [self unhook:@"initWithFrame:textContainer:"]; - // [self unhook:@"dealloc"]; - [self unhook:@"setSelectedRanges:affinity:stillSelecting"]; - [self unhook:@"selectAll:"]; - [self unhook:@"cut:"]; - [self unhook:@"copy:"]; - [self unhook:@"paste:"]; - [self unhook:@"delete:"]; - [self unhook:@"keyDown:"]; - [self unhook:@"mouseDown:"]; - [self unhook:@"drawRect:"]; - [self unhook:@"_drawInsertionPointInRect:color:"]; - [self unhook:@"_drawInsertionPointInRect:color:turnedOn:"]; - [self unhook:@"didChangeText"]; - [self unhook:@"viewDidMoveToSuperview"]; - [self unhook:@"shouldChangeTextInRange:replacementString"]; - // We do not unhook this too. Since "addObserver" is called in initWithCoder we should keep this hook - // (Calling observerValueForKeyPath in NSObject results in throwing exception) - //[self unhook:@"observeValueForKeyPath:ofObject:change:context:"]; -} - -#ifdef __XCODE5__ -- (id)initWithFrame:(NSRect)rect textContainer:(NSTextContainer *)container{ - TRACE_LOG(@"ENTER"); - DVTSourceTextView *base = (DVTSourceTextView*)self; - id obj = (DVTSourceTextViewHook*)[base initWithFrame_:rect textContainer:container]; - if( nil != obj ){ - [XVim.instance.options addObserver:obj forKeyPath:@"hlsearch" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - [XVim.instance.options addObserver:obj forKeyPath:@"ignorecase" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - [XVim.instance.searcher addObserver:obj forKeyPath:@"lastSearchString" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - } - return obj; -} - -- (id)initWithCoder:(NSCoder*)coder{ - DVTSourceTextView *base = (DVTSourceTextView*)self; - id obj = (DVTSourceTextViewHook*)[base initWithCoder_:coder]; - if( nil != obj ){ - [XVim.instance.options addObserver:obj forKeyPath:@"hlsearch" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - [XVim.instance.options addObserver:obj forKeyPath:@"ignorecase" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - [XVim.instance.searcher addObserver:obj forKeyPath:@"lastSearchString" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - } - return (DVTSourceTextViewHook*)[base initWithCoder_:coder]; -} -#else -- (id)initWithFrame:(NSRect)rect textContainer:(NSTextContainer *)container{ - TRACE_LOG(@"ENTER"); - DVTSourceTextView *base = (DVTSourceTextView*)self; - return (DVTSourceTextViewHook*)[base initWithFrame_:rect]; -} -- (id)initWithCoder:(NSCoder*)coder{ - DVTSourceTextView *base = (DVTSourceTextView*)self; - id obj = (DVTSourceTextViewHook*)[base initWithCoder_:coder]; - if( nil != obj ){ - [XVim.instance.options addObserver:obj forKeyPath:@"hlsearch" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - [XVim.instance.options addObserver:obj forKeyPath:@"ignorecase" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - [XVim.instance.searcher addObserver:obj forKeyPath:@"lastSearchString" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; - } - return obj; -} -#endif - -// This pragma is for suppressing warning that the dealloc method does not call [super dealloc]. ([base dealloc_] calls [super dealloc] so we do not need it) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wall" -- (void)dealloc{ - DVTSourceTextView *base = (DVTSourceTextView*)self; - @try{ - [XVim.instance.options removeObserver:self forKeyPath:@"hlsearch"]; - [XVim.instance.options removeObserver:self forKeyPath:@"ignorecase"]; - [XVim.instance.searcher removeObserver:self forKeyPath:@"lastSearchString"]; - } - @catch (NSException* exception){ - ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); - [Logger logStackTrace:exception]; - } - [base dealloc_]; - return; -} -#pragma GCC diagnostic pop - -- (void)setSelectedRanges:(NSArray *)ranges affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)flag{ - [(DVTSourceTextView*)self setSelectedRanges_:ranges affinity:affinity stillSelecting:flag]; - [(NSTextView*)self xvim_syncStateFromView]; -} - -- (void)selectAll:(id)sender{ - DVTSourceTextView *base = (DVTSourceTextView*)self; - XVimWindow* window = [base xvimWindow]; - [base selectAll_:sender]; - [window syncEvaluatorStack]; -} - -- (void)paste:(id)sender{ - DVTSourceTextView *base = (DVTSourceTextView*)self; - XVimWindow* window = [base xvimWindow]; - [base paste_:sender]; - [window syncEvaluatorStack]; - -} - -- (void)delete:(id)sender{ - DVTSourceTextView *base = (DVTSourceTextView*)self; - XVimWindow* window = [base xvimWindow]; - [base delete_:sender]; - [window syncEvaluatorStack]; -} - -- (void)keyDown:(NSEvent *)theEvent{ - @try{ - TRACE_LOG(@"Event:%@, XVimNotation:%@", theEvent.description, XVimKeyNotationFromXVimString([theEvent toXVimString])); - DVTSourceTextView *base = (DVTSourceTextView*)self; - XVimWindow* window = [base xvimWindow]; - if( nil == window ){ - [base keyDown_:theEvent]; - return; - } - - if( [window handleKeyEvent:theEvent] ){ - [base updateInsertionPointStateAndRestartTimer:YES]; - return; - } - // Call Original keyDown: - [base keyDown_:theEvent]; - return; - }@catch (NSException* exception) { - ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); - [Logger logStackTrace:exception]; - } - return; -} - -- (void)mouseDown:(NSEvent *)theEvent{ - @try{ - TRACE_LOG(@"Event:%@", theEvent.description); - DVTSourceTextView *base = (DVTSourceTextView*)self; - [base mouseDown_:theEvent]; - // When mouse down, NSTextView ( base in this case) takes the control of event loop internally - // and the method call above does not return immidiately and block until mouse up. mouseDragged: method is called from inside it but - // it never calls mouseUp: event. After mouseUp event is handled internally it returns the control. - // So the code here is executed AFTER mouseUp event is handled. - // At this point NSTextView changes its selectedRange so we usually have to sync XVim state. - - // TODO: To make it simple we should forward mouse events - // to handleKeyStroke as a special key stroke - // and the key stroke should be handled by the current evaluator. - XVimWindow* window = [base xvimWindow]; - [window syncEvaluatorStack]; - }@catch (NSException* exception) { - ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); - [Logger logStackTrace:exception]; - } - return; -} - - -- (void)drawRect:(NSRect)dirtyRect{ - @try{ - NSTextView* view = (NSTextView*)self; - - if( XVim.instance.options.hlsearch ){ - XVimMotion* lastSearch = [XVim.instance.searcher motionForRepeatSearch]; - if( nil != lastSearch.regex ){ - [view xvim_updateFoundRanges:lastSearch.regex withOption:lastSearch.option]; - } - }else{ - [view xvim_clearHighlightText]; - } - - - DVTSourceTextView *base = (DVTSourceTextView*)self; - [base drawRect_:dirtyRect]; - if( base.selectionMode != XVIM_VISUAL_NONE ){ - // NSTextView does not draw insertion point when selecting text. We have to draw insertion point by ourselves. - NSUInteger glyphIndex = [base insertionPoint]; - NSRect glyphRect = [base xvim_boundingRectForGlyphIndex:glyphIndex]; - [[[base insertionPointColor] colorWithAlphaComponent:0.5] set]; - NSRectFillUsingOperation( glyphRect, NSCompositeSourceOver); - } - }@catch (NSException* exception) { - ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); - [Logger logStackTrace:exception]; - } - return; -} - -// Drawing Caret -- (void)_drawInsertionPointInRect:(NSRect)aRect color:(NSColor*)aColor{ - @try{ - DVTSourceTextView *base = (DVTSourceTextView*)self; - XVimWindow* window = [base xvimWindow]; - - // We do not call original _darawInsertionPointRect here - // Because it uses NSRectFill to draw the caret which overrides the character entirely. - // We want some tranceparency for the caret. - - // [base _drawInsertionPointInRect_:glyphRect color:aColor]; - - // Call our drawing method - [window drawInsertionPointInRect:aRect color:aColor]; - - }@catch (NSException* exception) { - ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); - [Logger logStackTrace:exception]; - } -} - -- (void)drawInsertionPointInRect:(NSRect)rect color:(NSColor *)color turnedOn:(BOOL)flag{ - DVTSourceTextView *base = (DVTSourceTextView*)self; - // Call super class first. - [base drawInsertionPointInRect_:rect color:color turnedOn:flag]; - // Then tell the view to redraw to clear a caret. - if( !flag ){ - [base setNeedsDisplay:YES]; - } -} - -- (void)didChangeText{ - DVTSourceTextView *base = (DVTSourceTextView*)self; - [base setNeedsUpdateFoundRanges:YES]; - [base didChangeText_]; -} - -- (void)viewDidMoveToSuperview { - @try{ - DVTSourceTextView *base = (DVTSourceTextView*)self; - [base viewDidMoveToSuperview_]; - - // Hide scroll bars according to options - NSScrollView * scrollView = [base enclosingScrollView]; - [scrollView setPostsBoundsChangedNotifications:YES]; - }@catch (NSException* exception) { - ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); - [Logger logStackTrace:exception]; - } -} - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if([keyPath isEqualToString:@"ignorecase"] || [keyPath isEqualToString:@"hlsearch"] || [keyPath isEqualToString:@"lastSearchString"]){ - NSTextView* view = (NSTextView*)self; - [view setNeedsUpdateFoundRanges:YES]; - [view setNeedsDisplayInRect:[view visibleRect] avoidAdditionalLayout:YES]; - } -} - -@end - diff --git a/XVim/DVTTextStorage+XVimTextStoring.m b/XVim/DVTTextStorage+XVimTextStoring.m index 73c6d12d..cf59f040 100644 --- a/XVim/DVTTextStorage+XVimTextStoring.m +++ b/XVim/DVTTextStorage+XVimTextStoring.m @@ -10,6 +10,7 @@ #import "NSString+VimHelper.h" #import "DVTTextStorage+XVimTextStoring.h" #import "Logger.h" +#import "XVimBuffer.h" #if XVIM_XCODE_VERSION != 5 #define DVTTextStorage DVTSourceTextStorage @@ -17,6 +18,11 @@ @implementation DVTTextStorage (XVimTextStoring) +- (NSString *)xvim_string +{ + return self.string; +} + - (NSUInteger)xvim_numberOfLines { return self.numberOfLines; @@ -36,11 +42,10 @@ - (NSRange)xvim_indexRangeForLineNumber:(NSUInteger)num newLineLength:(NSUIntege { xvim_string_buffer_t sb; - NSAssert(num > 0, @"Line numbers start at 1"); if (num <= self.numberOfLines) { NSRange range = [self characterRangeForLineRange:NSMakeRange(num - 1, 1)]; - xvim_sb_init(&sb, self.xvim_string, range.location, range); + xvim_sb_init_range(&sb, self.xvim_string, range); xvim_sb_find_backward(&sb, [NSCharacterSet newlineCharacterSet]); if (newLineLength) *newLineLength = xvim_sb_range_to_end(&sb).length; @@ -53,21 +58,23 @@ - (NSRange)xvim_indexRangeForLineNumber:(NSUInteger)num newLineLength:(NSUIntege - (NSRange)xvim_indexRangeForLines:(NSRange)range { - NSAssert(range.location > 0, @"Line numbers start at 1"); - range.location--; return [self characterRangeForLineRange:range]; } - (NSUInteger)xvim_lineNumberAtIndex:(NSUInteger)index { - ASSERT_VALID_RANGE_WITH_EOF(index); if (index >= self.length) { return self.numberOfLines; } return [self lineRangeForCharacterRange:NSMakeRange(index, 0)].location + 1; } +- (void)xvim_indentCharacterRange:(NSRange)range buffer:(XVimBuffer *)buffer +{ + [self indentCharacterRange:range undoManager:buffer.undoManager]; +} + @end #if XVIM_XCODE_VERSION != 5 diff --git a/XVim/Hooker.h b/XVim/Hooker.h deleted file mode 100644 index 7e16921f..00000000 --- a/XVim/Hooker.h +++ /dev/null @@ -1,41 +0,0 @@ -// -// Hooker.h -// XVim -// -// Created by Shuichiro Suzuki on 2/3/12. -// Copyright 2012 JugglerShu.Net. All rights reserved. -// - -#import - -@interface Hooker : NSObject - -/** - * Hook method specified by "cls" and "mtd" by "cls2"-"mtd2. - * All the calls to the "cls"-"mtd" will be redirected to "cls2", "mtd2" - * When "mtd2" is called the "self" is "cls" object even its method in "cls2". - * If you want to call original method in "mtd2" write [self mtd_]; - * Here "mtd_" is the name of method generated automatically to keep original method and genereted with following rule. - * Originam Method Name Rule: - * If it does not have any arguments the method name is created by appending "_" to original method name. - * - "length" method would be "length_" - * - "textStorage" method would be "textStorage_". - * If it has arguments the method name is created by inserting "_" before the first ":" of original method name. - * - "initWithFrame:" would be "initWithFrame_:" - * - "setSelectedRange:affinity:stillSelecting:" would be "setSelectedRange_:affinity:stillSelecting:" - **/ -+ (void) hookClass:(NSString*)cls method:(NSString*)mtd byClass:(NSString*)cls2 method:(NSString*)mtd2; - -/** - * Unhook "cls"'s "mtd" method. - * This method must be hooked by hookClass:... method. - **/ -+ (void) unhookClass:(NSString*)cls method:(NSString*)mtd; - -/** - * This is actual implemenatation of hookClass:... method. - * Do not use this unless you need hook with special names ( not obeying the rule above ) - **/ -+ (void) hookMethod:(SEL)sel ofClass:(Class)cls withMethod:(Method)newMethod keepingOriginalWith:(SEL)selOriginal; - -@end diff --git a/XVim/Hooker.m b/XVim/Hooker.m deleted file mode 100644 index 69f32b84..00000000 --- a/XVim/Hooker.m +++ /dev/null @@ -1,79 +0,0 @@ -// -// Hooker.m -// XVim -// -// Created by Shuichiro Suzuki on 2/3/12. -// Copyright 2012 JugglerShu.Net. All rights reserved. -// - -#import "Hooker.h" - -@implementation Hooker - -- (id)init{ - self = [super init]; - if (self) { - // Initialization code here. - } - return self; -} - -// clsに渡されたクラスのselセレクタを呼び出したときに実行されるメソッドをフックし -// newMethodに転送するように設定する。 -// このとき、オリジナルのメソッドはselOriginalで渡されたセレクタで呼び出せるように設定する。 -// 例: hookMethod:@selector(keDown:) ofClass:[NSTextView class] withMethod:methodWrittenByMe keepingOriginalWith:@selector(originalKeyDown:) -// NSTextViewのkeyDown:セレクタでメソッドが呼ばれた時、methodWrittenByMe出指定されたメソッドが呼び出されるようになる。 -// methodWrittenByMeが呼び出されたとき、オリジナルのものを呼び出したければ、[self originalKeyDown:...]とすればよい -+ (void) hookMethod:(SEL)sel ofClass:(Class)cls withMethod:(Method)newMethod keepingOriginalWith:(SEL)selOriginal{ - //オリジナルメソッド superクラスも見に行きメソッドを探す - Method origMethod = class_getInstanceMethod(cls, sel); - //オリジナルIMP by セレクタ - IMP origImp_stret = class_getMethodImplementation_stret(cls, sel); - class_replaceMethod(cls, sel, method_getImplementation(newMethod), method_getTypeEncoding(origMethod)); - // origImpはnilが帰る可能性がある。(サブクラスがそのメソッドを持たない場合) origImp_stretをselOriginalで呼び出せるようにする - //NSTextViewの実装をkeyDown_:で呼び出せるようにしておく(keyDownをフックしたときに、転送できるように) - if( nil != selOriginal ){ - class_addMethod(cls, selOriginal, origImp_stret, method_getTypeEncoding(origMethod)); - } -} - -+ (void) hookClass:(NSString*)cls method:(NSString*)mtd byClass:(NSString*)cls2 method:(NSString*)mtd2{ - Class c1 = NSClassFromString(cls); - Class c2 = NSClassFromString(cls2); - Method m2 = class_getInstanceMethod(c2, NSSelectorFromString(mtd2)); - - SEL preservedSelector = [Hooker createPreserveSelectorName:mtd]; - - [Hooker hookMethod:NSSelectorFromString(mtd) ofClass:c1 withMethod:m2 keepingOriginalWith:preservedSelector]; -} - -+ (void) unhookClass:(NSString*)cls method:(NSString*)mtd{ - Class c1 = NSClassFromString(cls); - SEL preservedSelector = [Hooker createPreserveSelectorName:mtd]; - Method m2 = class_getInstanceMethod(c1, preservedSelector); - - [Hooker hookMethod:NSSelectorFromString(mtd) ofClass:c1 withMethod:m2 keepingOriginalWith:nil]; -} - -/** - * Internal method. - * This convert a method name by a rule explained in hookClass:... method's explanation. - * It is like... - * Method "foo" will be "foo_" - * Method "foo:" will be "foo_:" - * Method "foo:bar:" will be "foo_:bar" - **/ -+ (SEL) createPreserveSelectorName:(NSString*)origSelector{ - NSRange r = [origSelector rangeOfString:@":"]; - if( NSNotFound == r.location ){ - // Just appeend "_" at the end. - return NSSelectorFromString([origSelector stringByAppendingString:@"_"]); - }else{ - // Insert "_" before first ":" - NSMutableString *newSel = [NSMutableString stringWithString:[origSelector substringToIndex:r.location]]; - [newSel appendString:@"_"]; - [newSel appendString:[origSelector substringFromIndex:r.location]]; - return NSSelectorFromString(newSel); - } -} -@end diff --git a/XVim/IDEEditorHook.h b/XVim/IDEEditor+XVim.h similarity index 54% rename from XVim/IDEEditorHook.h rename to XVim/IDEEditor+XVim.h index 6a6edc2e..81fc373f 100644 --- a/XVim/IDEEditorHook.h +++ b/XVim/IDEEditor+XVim.h @@ -9,11 +9,9 @@ #import #import "IDEKit.h" -@interface IDEEditorHook : NSViewController -+(void) hook; -@end +@interface IDEEditor (XVim) -@interface IDEEditor(Hook) -- (void)didSetupEditor_; -- (void)primitiveInvalidate_; -@end ++ (void)xvim_initialize; + +- (void)xvim_tryToSetupXVimView; +@end \ No newline at end of file diff --git a/XVim/IDEEditor+XVim.m b/XVim/IDEEditor+XVim.m new file mode 100644 index 00000000..28c871b0 --- /dev/null +++ b/XVim/IDEEditor+XVim.m @@ -0,0 +1,152 @@ +// +// IDEEditor.m +// XVim +// +// Created by Suzuki Shuichiro on 5/1/12. +// Copyright (c) 2012 JugglerShu.Net. All rights reserved. +// + +#import +#import "XVim.h" +#import "XVimWindow.h" +#import "XVimStatusLine.h" +#import "IDESourceEditor.h" + +#import "IDEKit.h" +#import "IDEEditor+XVim.h" +#import "NSObject+XVimAdditions.h" +#import "DVTSourceTextView+XVim.h" +#import "Logger.h" + +static char const * const DID_REGISTER_OBSERVER_KEY = "net.JugglerShu.IDEEditorHook._didRegisterObserver"; + + +@interface IDESourceCodeVersionsLogSubmode () +- (void)xvim_setPrimaryEditor:(IDEEditor *)primaryEditor; +@end + +@interface IDESourceCodeVersionsBlameSubmode () +- (void)xvim_setPrimaryEditor:(IDEEditor *)primaryEditor; +@end + +@interface IDESourceCodeVersionsTwoUpSubmode () +- (void)xvim_setPrimaryEditor:(IDEEditor *)primaryEditor; +- (void)xvim_setSecondaryEditor:(IDEEditor *)secondaryEditor; +@end + +static void xvim_setPrimaryEditor(id self, SEL _cmd, IDEEditor *editor) +{ + [self xvim_setPrimaryEditor:editor]; + [editor xvim_tryToSetupXVimView]; +} + +static void xvim_setSecondaryEditor(id self, SEL _cmd, IDEEditor *editor) +{ + [self xvim_setSecondaryEditor:editor]; + [editor xvim_tryToSetupXVimView]; +} + +@implementation IDEEditor (XVim) + ++ (void)xvim_initialize +{ + if (self == [IDEEditor class]) { + [NSClassFromString(@"IDESourceCodeVersionsLogSubmode") + xvim_swizzleInstanceMethod:@selector(setPrimaryEditor:) + with:@selector(xvim_setPrimaryEditor:) + imp:(IMP)xvim_setPrimaryEditor]; + + [NSClassFromString(@"IDESourceCodeVersionsBlameSubmode") + xvim_swizzleInstanceMethod:@selector(setPrimaryEditor:) + with:@selector(xvim_setPrimaryEditor:) + imp:(IMP)xvim_setPrimaryEditor]; + + [NSClassFromString(@"IDESourceCodeVersionsTwoUpSubmode") + xvim_swizzleInstanceMethod:@selector(setPrimaryEditor:) + with:@selector(xvim_setPrimaryEditor:) + imp:(IMP)xvim_setPrimaryEditor]; + + [NSClassFromString(@"IDESourceCodeVersionsTwoUpSubmode") + xvim_swizzleInstanceMethod:@selector(setSecondaryEditor:) + with:@selector(xvim_setSecondaryEditor:) + imp:(IMP)xvim_setSecondaryEditor]; + + [self xvim_swizzleInstanceMethod:@selector(didSetupEditor) + with:@selector(xvim_didSetupEditor)]; + [self xvim_swizzleInstanceMethod:@selector(primitiveInvalidate) + with:@selector(xvim_primitiveInvalidate)]; + } +} + +- (void)xvim_tryToSetupXVimView +{ + if ([self isKindOfClass:NSClassFromString(@"IDESourceCodeEditor")]) { + DVTSourceTextView *tv = (DVTSourceTextView *)[(id)self textView]; + XVimWindow *window = tv.xvimWindow; + + if (window && self.document.fileURL.isFileURL) { + DVTTextStorage *ts = [tv textStorage]; + if (ts && !ts.xvim_buffer) { + [XVimBuffer makeBufferForDocument:self.document textStorage:ts]; + } + if (tv && !tv.xvim_view) { + [tv xvim_makeXVimViewInWindow:window]; + } + } + } +} + +- (void)xvim_didSetupEditor +{ + [self xvim_didSetupEditor]; + + if ([self isKindOfClass:NSClassFromString(@"IDESourceCodeEditor")]) { + [self xvim_tryToSetupXVimView]; + } else if ([self isKindOfClass:[IDEComparisonEditor class]]) { + [[[(IDEComparisonEditor *)self submode] primaryEditor] xvim_tryToSetupXVimView]; + [[[(IDEComparisonEditor *)self submode] secondaryEditor] xvim_tryToSetupXVimView]; + } + + // If you do not like status line comment out folloing. + // ---- FROM HERE ---- + NSView *container = nil; + + if ([self isKindOfClass:NSClassFromString(@"IDESourceCodeComparisonEditor")]) { + container = [(IDESourceCodeComparisonEditor*)self layoutView]; + } else if ([self isKindOfClass:NSClassFromString(@"IDESourceCodeEditor")]) { + container = [(IDESourceCodeEditor*)self containerView]; + } else { + return; + } + + if (container != nil) { + XVimStatusLine *status = [XVimStatusLine associateOf:container]; + if (status == nil) { + // Insert status line + [container setPostsFrameChangedNotifications:YES]; + status = [[[XVimStatusLine alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)] autorelease]; + [container addSubview:status]; + [status associateWith:container]; + + // Layout + [[NSNotificationCenter defaultCenter] addObserver:status selector:@selector(didContainerFrameChanged:) name:NSViewFrameDidChangeNotification object:container]; + [status layoutStatus:container]; + [container performSelector:@selector(invalidateLayout)]; + + // For % register and to notify contents of editor is changed + [self addObserver:[XVim instance] forKeyPath:@"document" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; + objc_setAssociatedObject(self, DID_REGISTER_OBSERVER_KEY, @YES, OBJC_ASSOCIATION_ASSIGN); + } + } + //---- TO HERE ---- +} + +- (void)xvim_primitiveInvalidate +{ + if (objc_getAssociatedObject(self, DID_REGISTER_OBSERVER_KEY)) { + [self removeObserver:[XVim instance] forKeyPath:@"document"]; + } + [self xvim_primitiveInvalidate]; +} + +@end diff --git a/XVim/IDEEditorArea+XVim.h b/XVim/IDEEditorArea+XVim.h index 801b162f..bd7861e6 100644 --- a/XVim/IDEEditorArea+XVim.h +++ b/XVim/IDEEditorArea+XVim.h @@ -7,7 +7,8 @@ // #import "IDEKit.h" -#import "XVimCommandLine.h" + +@class XVimWindow; /* * This is the extension of IDEEditorArea class in Xcode. @@ -15,9 +16,9 @@ * See IDEKit.h to refer original IDEEditorArea class. */ @interface IDEEditorArea (XVim) -@property (readonly, strong) XVimWindow *xvimWindow; -- (void)setupXVim; -- (void)teardownXVim; +@property (nonatomic, readonly) XVimWindow *xvim_window; + ++ (void)xvim_initialize; @end diff --git a/XVim/IDEEditorArea+XVim.m b/XVim/IDEEditorArea+XVim.m index 4bf59270..a99a6402 100644 --- a/XVim/IDEEditorArea+XVim.m +++ b/XVim/IDEEditorArea+XVim.m @@ -8,18 +8,39 @@ #import #import "IDEEditorArea+XVim.h" +#import "NSObject+XVimAdditions.h" #import "XVimWindow.h" -static const char *KEY_WINDOW = "xvimwindow"; - +static const char *KEY_WINDOW = "xvimwindow"; + +/** + * IDEEditorArea is a area including primary editor and assistant editor and debug area (The view right of the navigator) + * This class hooks IDEEditorArea and does some works. + * "viewDidInstall" is called when the view setup is done ( as far as I saw the behaviour ). + * This class has private instance variable named "_editorAreaAutoLayoutView" which is the view + * contains source code editores and border view between editors and debug area. + * We insert command line view between editors and debug area. + * + * IDEEdiatorArea exists in every Xcode tabs so if you have 4 tabs in a Xcode window there are 4 command line and XVimWindow views we insert. + */ @implementation IDEEditorArea (XVim) -- (XVimWindow *)xvimWindow ++ (void)xvim_initialize +{ + if (self == [IDEEditorArea class]) { + [self xvim_swizzleInstanceMethod:@selector(viewDidInstall) + with:@selector(xvim_viewDidInstall)]; + [self xvim_swizzleInstanceMethod:@selector(primitiveInvalidate) + with:@selector(xvim_primitiveInvalidate)]; + } +} + +- (XVimWindow *)xvim_window { return objc_getAssociatedObject(self, KEY_WINDOW); } -- (NSView *)textViewArea +- (NSView *)_xvim_editorAreaAutoLayoutView { NSView *layoutView; @@ -28,7 +49,7 @@ - (NSView *)textViewArea return layoutView; } -- (DVTBorderedView *)debuggerBarBorderedView +- (DVTBorderedView *)_xvim_debuggerBarBorderedView { DVTBorderedView *border; @@ -37,10 +58,12 @@ - (DVTBorderedView *)debuggerBarBorderedView return border; } -- (void)setupXVim +- (void)xvim_viewDidInstall { + [self xvim_viewDidInstall]; + XVimWindow *xvim = [[XVimWindow alloc] initWithIDEEditorArea:self]; - NSView *layoutView = [self textViewArea]; + NSView *layoutView = [self _xvim_editorAreaAutoLayoutView]; XVimCommandLine *cmd = xvim.commandLine; [layoutView addSubview:cmd]; @@ -51,7 +74,7 @@ - (void)setupXVim name:NSViewFrameDidChangeNotification object:layoutView]; if (layoutView.subviews.count > 0) { - DVTBorderedView *border = [self debuggerBarBorderedView]; + DVTBorderedView *border = [self _xvim_debuggerBarBorderedView]; // We need to know if border view is hidden or not to place editors and command line correctly. [border addObserver:cmd forKeyPath:@"hidden" options:NSKeyValueObservingOptionNew context:nil]; @@ -61,13 +84,15 @@ - (void)setupXVim [xvim release]; } -- (void)teardownXVim +- (void)xvim_primitiveInvalidate { - XVimCommandLine *cmd = self.xvimWindow.commandLine; - DVTBorderedView *border = [self debuggerBarBorderedView]; + XVimCommandLine *cmd = self.xvim_window.commandLine; + DVTBorderedView *border = [self _xvim_debuggerBarBorderedView]; [border removeObserver:cmd forKeyPath:@"hidden"]; [[NSNotificationCenter defaultCenter] removeObserver:cmd]; + + [self xvim_primitiveInvalidate]; } @end diff --git a/XVim/IDEEditorAreaHook.h b/XVim/IDEEditorAreaHook.h deleted file mode 100644 index 57c40366..00000000 --- a/XVim/IDEEditorAreaHook.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// XVimEditorArea.h -// XVim -// -// Created by Shuichiro Suzuki on 4/23/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import -#import "IDEKit.h" - -@interface IDEEditorAreaHook : NSObject -+ (void)hook; -@end - - -@interface IDEEditorArea(Hook) -- (void)viewDidInstall_; -- (void)primitiveInvalidate_; -@end \ No newline at end of file diff --git a/XVim/IDEEditorAreaHook.m b/XVim/IDEEditorAreaHook.m deleted file mode 100644 index aaabc93c..00000000 --- a/XVim/IDEEditorAreaHook.m +++ /dev/null @@ -1,50 +0,0 @@ -// -// XVimEditorArea.m -// XVim -// -// Created by Shuichiro Suzuki on 4/23/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import "IDEEditorAreaHook.h" -#import "Hooker.h" -#import "DVTKit.h" -#import "IDEEditorArea+XVim.h" - -@implementation IDEEditorAreaHook -/** - * IDEEditorArea is a area including primary editor and assistant editor and debug area (The view right of the navigator) - * This class hooks IDEEditorArea and does some works. - * "viewDidInstall" is called when the view setup is done ( as far as I saw the behaviour ). - * This class has private instance variable named "_editorAreaAutoLayoutView" which is the view - * contains source code editores and border view between editors and debug area. - * We insert command line view between editors and debug area. - * - * IDEEdiatorArea exists in every Xcode tabs so if you have 4 tabs in a Xcode window there are 4 command line and XVimWindow views we insert. - **/ - -+(void)hook -{ - Class c = NSClassFromString(@"IDEEditorArea"); - - [Hooker hookMethod:@selector(viewDidInstall) ofClass:c withMethod:class_getInstanceMethod([self class], @selector(viewDidInstall) ) keepingOriginalWith:@selector(viewDidInstall_)]; - [Hooker hookMethod:@selector(primitiveInvalidate) ofClass:c withMethod:class_getInstanceMethod([self class], @selector(primitiveInvalidate)) keepingOriginalWith:@selector(primitiveInvalidate_)]; -} - -- (void)viewDidInstall -{ - IDEEditorArea *base = (IDEEditorArea *)self; - - [base viewDidInstall_]; - [base setupXVim]; -} - -- (void)primitiveInvalidate -{ - IDEEditorArea *base = (IDEEditorArea*)self; - - [base teardownXVim]; - [base primitiveInvalidate_]; -} - -@end diff --git a/XVim/IDEEditorHook.m b/XVim/IDEEditorHook.m deleted file mode 100644 index b867c928..00000000 --- a/XVim/IDEEditorHook.m +++ /dev/null @@ -1,77 +0,0 @@ -// -// IDEEditor.m -// XVim -// -// Created by Suzuki Shuichiro on 5/1/12. -// Copyright (c) 2012 JugglerShu.Net. All rights reserved. -// - -#import "IDEEditorHook.h" -#import "IDEKit.h" -#import "IDESourceEditor.h" -#import "Hooker.h" -#import "Logger.h" -#import "XVim.h" -#import "XVimStatusLine.h" -#import - -#define DID_REGISTER_OBSERVER_KEY "net.JugglerShu.IDEEditorHook._didRegisterObserver" - -@implementation IDEEditorHook -+(void)hook{ - Class c = NSClassFromString(@"IDEEditor"); - - [Hooker hookMethod:@selector(didSetupEditor) ofClass:c withMethod:class_getInstanceMethod([self class], @selector(didSetupEditor) ) keepingOriginalWith:@selector(didSetupEditor_)]; - [Hooker hookMethod:@selector(primitiveInvalidate) ofClass:c withMethod:class_getInstanceMethod([self class], @selector(primitiveInvalidate)) keepingOriginalWith:@selector(primitiveInvalidate_)]; -} - -- (void)didSetupEditor{ - - IDEEditor* editor = (IDEEditor*)self; - [editor didSetupEditor_]; - - // If you do not like status line comment out folloing. - // ---- FROM HERE ---- - NSView* container = nil; - if( [NSStringFromClass([editor class]) isEqualToString:@"IDESourceCodeComparisonEditor"] ){ - container = [(IDESourceCodeComparisonEditor*)editor layoutView]; - } - else if( [NSStringFromClass([editor class]) isEqualToString:@"IDESourceCodeEditor"] ){ - container = [(IDESourceCodeEditor*)editor containerView]; - }else{ - return; - } - - if (container != nil) { - XVimStatusLine *status = [XVimStatusLine associateOf:container]; - if (status == nil) { - // Insert status line - [container setPostsFrameChangedNotifications:YES]; - status = [[[XVimStatusLine alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)] autorelease]; - [container addSubview:status]; - [status associateWith:container]; - - // Layout - [[NSNotificationCenter defaultCenter] addObserver:status selector:@selector(didContainerFrameChanged:) name:NSViewFrameDidChangeNotification object:container]; - [status layoutStatus:container]; - [container performSelector:@selector(invalidateLayout)]; - - // For % register and to notify contents of editor is changed - [editor addObserver:[XVim instance] forKeyPath:@"document" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; - objc_setAssociatedObject(editor, DID_REGISTER_OBSERVER_KEY, [NSNumber numberWithBool:YES], OBJC_ASSOCIATION_RETAIN); - } - } - //---- TO HERE ---- -} - -- (void)primitiveInvalidate { - IDEEditor *editor = (IDEEditor *)self; - NSNumber *didRegisterObserver = objc_getAssociatedObject(editor, DID_REGISTER_OBSERVER_KEY); - if ([didRegisterObserver boolValue]) { - [editor removeObserver:[XVim instance] forKeyPath:@"document"]; - } - - [editor primitiveInvalidate_]; -} - -@end diff --git a/XVim/IDESourceCodeEditorHook.h b/XVim/IDESourceCodeEditorHook.h deleted file mode 100644 index 16d1f5c0..00000000 --- a/XVim/IDESourceCodeEditorHook.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// XVimSourceCodeEditor.h -// XVim -// -// Created by Tomas Lundell on 31/03/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import -#import "IDESourceEditor.h" -@interface IDESourceCodeEditorHook : NSObject -+ (void)hook; -@end - -@interface IDESourceCodeEditor(Hook) -- (id)initWithNibName_:nibName bundle:nibBundle document:nibDocument; -@end \ No newline at end of file diff --git a/XVim/IDESourceCodeEditorHook.m b/XVim/IDESourceCodeEditorHook.m deleted file mode 100644 index 09f7fe26..00000000 --- a/XVim/IDESourceCodeEditorHook.m +++ /dev/null @@ -1,32 +0,0 @@ -// -// XVimSourceCodeEditor.m -// XVim -// -// Created by Tomas Lundell on 31/03/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import "IDESourceCodeEditorHook.h" -#import "IDEKit.h" -#import "XVimWindow.h" -#import "Hooker.h" -#import "Logger.h" -#import "XVimStatusLine.h" -#import "XVim.h" - -@implementation IDESourceCodeEditorHook - -+ (void) hook -{ - Class delegate = NSClassFromString(@"IDESourceCodeEditor"); - [Hooker hookMethod:@selector(textView:willChangeSelectionFromCharacterRanges:toCharacterRanges:) - ofClass:delegate - withMethod:class_getInstanceMethod([self class], @selector(textView:willChangeSelectionFromCharacterRanges:toCharacterRanges:)) - keepingOriginalWith:@selector(textView_:willChangeSelectionFromCharacterRanges:toCharacterRanges:)]; -} - -- (NSArray*) textView:(NSTextView *)textView willChangeSelectionFromCharacterRanges:(NSArray *)oldSelectedCharRanges toCharacterRanges:(NSArray *)newSelectedCharRanges -{ - return newSelectedCharRanges; -} -@end \ No newline at end of file diff --git a/XVim/IDEWorkspaceWindowHook.h b/XVim/IDEWorkspaceWindowHook.h deleted file mode 100644 index 15cf15ef..00000000 --- a/XVim/IDEWorkspaceWindowHook.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// IDEWorkspaceWindowHook.h -// XVim -// -// Created by Suzuki Shuichiro on 3/24/13. -// -// - -#import - -@interface IDEWorkspaceWindowHook : NSObject -+(void)hook; -@end - -@interface IDEWorkspaceWindow(hook) -- (void)sendEvent_:(NSEvent*)event; -@end \ No newline at end of file diff --git a/XVim/IDEWorkspaceWindowHook.m b/XVim/IDEWorkspaceWindowHook.m deleted file mode 100644 index 8ed96ca0..00000000 --- a/XVim/IDEWorkspaceWindowHook.m +++ /dev/null @@ -1,27 +0,0 @@ -// -// IDEWorkspaceWindowHook.m -// XVim -// -// Created by Suzuki Shuichiro on 3/24/13. -// -// - -#import "IDEKit.h" -#import "IDEWorkspaceWindowHook.h" -#import "Hooker.h" -#import "Logger.h" -#import "NSEvent+VimHelper.h" - -@implementation IDEWorkspaceWindowHook -+(void)hook{ - [Hooker hookClass:@"IDEWorkspaceWindow" method:@"sendEvent:" byClass:@"IDEWorkspaceWindowHook" method:@"sendEvent:"]; -} - --(void)sendEvent:(NSEvent*)event{ - IDEWorkspaceWindow* base = (IDEWorkspaceWindow*)self; - if( event.type == NSKeyDown ){ - TRACE_LOG(@"Window:%p keyCode:%d characters:%@ charsIgnoreMod:%@ cASCII:%d", self, event.keyCode, event.characters, event.charactersIgnoringModifiers, event.unmodifiedKeyCode); - } - [base sendEvent_:event]; -} -@end diff --git a/XVim/Info_Xcode5.plist b/XVim/Info_Xcode5.plist index 5c601dc1..40eff009 100644 --- a/XVim/Info_Xcode5.plist +++ b/XVim/Info_Xcode5.plist @@ -22,6 +22,7 @@ 37B30044-3B14-46BA-ABAA-F01000C27B63 63FC1C47-140D-42B0-BB4D-A10B2D225574 + 640F884E-CE55-4B40-87C0-8869546CAB7A NSHumanReadableCopyright Copyright © 2012 JugglerShu.Net. All rights reserved. @@ -29,8 +30,6 @@ ${PRODUCT_NAME} XC4Compatible - XCGCReady - XCPluginHasUI diff --git a/XVim/Logger.m b/XVim/Logger.m index 7508482e..60cb68ce 100644 --- a/XVim/Logger.m +++ b/XVim/Logger.m @@ -6,8 +6,8 @@ // Copyright 2011 JugglerShu.Net. All rights reserved. // +#import "XVim.h" #import "Logger.h" -#import "Hooker.h" #import #import @@ -158,9 +158,21 @@ - (void) setLogFile:(NSString *)path{ } + (void) logStackTrace:(NSException*)ex{ + NSMutableString *s = [[NSMutableString alloc] init]; + + [s appendFormat:@"Exception %@ thrown { reason = %@ }\n", ex.name, ex.reason]; + [ex.userInfo enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + [s appendFormat:@" %@: %@\n", key, obj]; + }]; + [s appendFormat:@"Call Stack:\n"]; for( NSString* e in [ex callStackSymbols]){ TRACE_LOG(@"%@", e); + [s appendFormat:@" %@\n", e]; } +#ifdef DEBUG + [[XVim instance] writeToConsole:s]; +#endif + [s release]; } + (void) traceMethodList:(NSString*)class{ diff --git a/XVim/NSCharacterSet+XVimAdditions.h b/XVim/NSCharacterSet+XVimAdditions.h new file mode 100644 index 00000000..d2ae7cd4 --- /dev/null +++ b/XVim/NSCharacterSet+XVimAdditions.h @@ -0,0 +1,16 @@ +// +// NSCharacterSet+XVimAdditions.h +// XVim +// +// Created by John AppleSeed on 25/11/13. +// +// + +#import + +@interface NSCharacterSet (XVimAdditions) + ++ (NSCharacterSet *)xvim_octDigitsCharacterSet; ++ (NSCharacterSet *)xvim_hexDigitsCharacterSet; + +@end diff --git a/XVim/NSCharacterSet+XVimAdditions.m b/XVim/NSCharacterSet+XVimAdditions.m new file mode 100644 index 00000000..c8288a74 --- /dev/null +++ b/XVim/NSCharacterSet+XVimAdditions.m @@ -0,0 +1,41 @@ +// +// NSCharacterSet+XVimAdditions.m +// XVim +// +// Created by John AppleSeed on 25/11/13. +// +// + +#import "NSCharacterSet+XVimAdditions.h" + +@implementation NSCharacterSet (XVimAdditions) + +static NSCharacterSet *_xvimHexDigits; +static NSCharacterSet *_xvimOctDigits; + +NS_INLINE void _xvim_init_charsets(void) +{ + static dispatch_once_t once; + dispatch_once(&once, ^{ +#define CS(s) [[NSCharacterSet characterSetWithCharactersInString:s] retain] + + _xvimHexDigits = CS(@"0123456789abcdefABCDEF"); + _xvimOctDigits = CS(@"01234567"); + +#undef CS + }); +} + ++ (NSCharacterSet *)xvim_octDigitsCharacterSet +{ + _xvim_init_charsets(); + return _xvimOctDigits; +} + ++ (NSCharacterSet *)xvim_hexDigitsCharacterSet +{ + _xvim_init_charsets(); + return _xvimHexDigits; +} + +@end diff --git a/XVim/NSObject+ExtraData.h b/XVim/NSObject+ExtraData.h deleted file mode 100644 index 38aeea34..00000000 --- a/XVim/NSObject+ExtraData.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// NSObject+ExtraData.h -// XVim -// -// Created by Suzuki Shuichiro on 3/24/13. -// -// - -#import - -@interface NSObject (ExtraData) -- (id)dataForName:(NSString*)name; -- (void)setData:(id)data forName:(NSString*)name; - -// Utilities -- (void)setBool:(BOOL)b forName:(NSString*)name; -- (void)setUnsignedInteger:(NSUInteger)b forName:(NSString*)name; -- (void)setInteger:(NSInteger)b forName:(NSString *)name; -@end diff --git a/XVim/NSObject+ExtraData.m b/XVim/NSObject+ExtraData.m deleted file mode 100644 index 4635b2c4..00000000 --- a/XVim/NSObject+ExtraData.m +++ /dev/null @@ -1,57 +0,0 @@ -// -// NSObject+ExtraData.m -// XVim -// -// Created by Suzuki Shuichiro on 3/24/13. -// -// - -#import "NSObject+ExtraData.h" -#import -#import "Logger.h" - -static const NSString* EXTRA_DATA_KEY = @"EXTRADATAKEY"; -@implementation NSObject (ExtraData) - -- (id)dataForName:(NSString*)name{ - NSMutableDictionary* dic = objc_getAssociatedObject(self , EXTRA_DATA_KEY); - if( nil == dic ){ - return nil; - } - - id ret = [dic objectForKey:name]; - if( [NSNull null] == ret ){ - return nil; - }else{ - return ret; - } -} - -- (void)setData:(id)data forName:(NSString*)name{ - NSMutableDictionary* dic = objc_getAssociatedObject(self , EXTRA_DATA_KEY); - if( nil == dic ){ - dic = [NSMutableDictionary dictionary]; - objc_setAssociatedObject(self, EXTRA_DATA_KEY, dic, OBJC_ASSOCIATION_RETAIN); - } - - if( nil == data){ - data = [NSNull null]; - } - [dic setObject:data forKey:name]; -} - -- (void)setBool:(BOOL)b forName:(NSString *)name{ - NSNumber* n = [NSNumber numberWithBool:b]; - [self setData:n forName:name]; -} - -- (void)setUnsignedInteger:(NSUInteger)b forName:(NSString *)name{ - NSNumber* n = [NSNumber numberWithUnsignedInteger:b]; - [self setData:n forName:name]; -} - -- (void)setInteger:(NSInteger)b forName:(NSString *)name{ - NSNumber* n = [NSNumber numberWithInteger:b]; - [self setData:n forName:name]; -} -@end diff --git a/XVim/NSObject+XVimAdditions.h b/XVim/NSObject+XVimAdditions.h new file mode 100644 index 00000000..d8d49740 --- /dev/null +++ b/XVim/NSObject+XVimAdditions.h @@ -0,0 +1,30 @@ +// +// NSObject+XVimAdditions.h +// XVim +// +// Created by John AppleSeed on 21/11/13. +// +// + +#import + +@interface NSObject (XVimAdditions) + +/** @brief swizzles class selector \a origSel with \a newSel. + * + * @param origSel the name of the class method selector to swizzle + * @param newSel the name of the class method selector to use as a replacement + */ ++ (void)xvim_swizzleClassMethod:(SEL)origSel with:(SEL)newSel; ++ (void)xvim_swizzleClassMethod:(SEL)origSel with:(SEL)newSel imp:(IMP)imp; + +/** @brief swizzles instance selector \a origSel with \a newSel. + * + * @param origSel the name of the instance method selector to swizzle + * @param newSel the name of the instance method selector to use as a replacement + * + */ ++ (void)xvim_swizzleInstanceMethod:(SEL)origSel with:(SEL)newSel; ++ (void)xvim_swizzleInstanceMethod:(SEL)origSel with:(SEL)newSel imp:(IMP)imp; + +@end diff --git a/XVim/NSObject+XVimAdditions.m b/XVim/NSObject+XVimAdditions.m new file mode 100644 index 00000000..19f53edc --- /dev/null +++ b/XVim/NSObject+XVimAdditions.m @@ -0,0 +1,69 @@ +// +// NSObject+XVimAdditions.m +// XVim +// +// Created by John AppleSeed on 21/11/13. +// +// + +#import +#import "NSObject+XVimAdditions.h" + +@implementation NSObject (XVimAdditions) + ++ (void)xvim_swizzleClassMethod:(SEL)origSel with:(SEL)newSel +{ + Method origMethod = class_getClassMethod(self, origSel); + Method newMethod = class_getClassMethod(self, newSel); + Class class = object_getClass(self); + + NSAssert(origMethod, @"+[%@ %@] doesn't exist", NSStringFromClass(self), NSStringFromSelector(origSel)); + NSAssert(newMethod, @"+[%@ %@] doesn't exist", NSStringFromClass(self), NSStringFromSelector(newSel)); + + if (class_addMethod(class, origSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) { + class_replaceMethod(class, newSel, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); + } else { + method_exchangeImplementations(newMethod, origMethod); + } +} + ++ (void)xvim_swizzleClassMethod:(SEL)origSel with:(SEL)newSel imp:(IMP)imp +{ + Method origMethod = class_getClassMethod(self, origSel); + Method newMethod = class_getClassMethod(self, newSel); + + NSAssert(origMethod, @"+[%@ %@] doesn't exist", NSStringFromClass(self), NSStringFromSelector(origSel)); + NSAssert(!newMethod, @"+[%@ %@] exists", NSStringFromClass(self), NSStringFromSelector(newSel)); + + class_addMethod(self, newSel, imp, method_getTypeEncoding(origMethod)); + [self xvim_swizzleClassMethod:origSel with:newSel]; +} + ++ (void)xvim_swizzleInstanceMethod:(SEL)origSel with:(SEL)newSel +{ + Method origMethod = class_getInstanceMethod(self, origSel); + Method newMethod = class_getInstanceMethod(self, newSel); + + NSAssert(origMethod, @"-[%@ %@] doesn't exist", NSStringFromClass(self), NSStringFromSelector(origSel)); + NSAssert(newMethod, @"-[%@ %@] doesn't exist", NSStringFromClass(self), NSStringFromSelector(newSel)); + + if (class_addMethod(self, origSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) { + class_replaceMethod(self, newSel, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); + } else { + method_exchangeImplementations(newMethod, origMethod); + } +} + ++ (void)xvim_swizzleInstanceMethod:(SEL)origSel with:(SEL)newSel imp:(IMP)imp +{ + Method origMethod = class_getInstanceMethod(self, origSel); + Method newMethod = class_getInstanceMethod(self, newSel); + + NSAssert(origMethod, @"-[%@ %@] doesn't exist", NSStringFromClass(self), NSStringFromSelector(origSel)); + NSAssert(!newMethod, @"-[%@ %@] exists", NSStringFromClass(self), NSStringFromSelector(newSel)); + + class_addMethod(self, newSel, imp, method_getTypeEncoding(origMethod)); + [self xvim_swizzleInstanceMethod:origSel with:newSel]; +} + +@end diff --git a/XVim/NSString+VimHelper.h b/XVim/NSString+VimHelper.h index f9399e32..0468e001 100644 --- a/XVim/NSString+VimHelper.h +++ b/XVim/NSString+VimHelper.h @@ -9,8 +9,6 @@ #import BOOL isDigit(unichar ch); -BOOL isOctDigit(unichar ch); -BOOL isHexDigit(unichar ch); BOOL isAlpha(unichar ch); BOOL isDelimeter(unichar ch); BOOL isWhitespace(unichar ch); @@ -19,13 +17,6 @@ BOOL isNonblank(unichar ch); BOOL isKeyword(unichar ch); @interface NSString (VimHelper) -- (BOOL) isDigit:(NSUInteger)index; -- (BOOL) isOctDigit:(NSUInteger)index; -- (BOOL) isHexDigit:(NSUInteger)index; -- (BOOL) isAlpha:(NSUInteger)index; -- (BOOL) isDelimeter:(NSUInteger)index; -- (BOOL) isNewline:(NSUInteger)index; -- (BOOL) isKeyword:(NSUInteger)index; /** * Convert Vim regex to ICU regex. @@ -35,9 +26,13 @@ BOOL isKeyword(unichar ch); **/ - (NSString*)convertToICURegex:(NSRegularExpressionOptions*)options; +- (NSMutableString *)newMutableSubstringWithRange:(NSRange)range; + + (NSString *)stringMadeOfSpaces:(NSUInteger)count; + @end @interface NSMutableString (VimHelper) + (NSMutableString *)mutableStringMadeOfSpaces:(NSUInteger)count; +- (void)appendCharacters:(const unichar *)chars length:(NSUInteger)length; @end diff --git a/XVim/NSString+VimHelper.m b/XVim/NSString+VimHelper.m index 1593d683..065fa52c 100644 --- a/XVim/NSString+VimHelper.m +++ b/XVim/NSString+VimHelper.m @@ -11,8 +11,6 @@ // support functions // ///////////////////////// BOOL isDigit(unichar ch) { return ch >= '0' && ch <= '9'; } -BOOL isOctDigit(unichar ch) { return ch >= '0' && ch <= '7'; } -BOOL isHexDigit(unichar ch) { return isDigit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'); } BOOL isWhitespace(unichar ch) { return [[NSCharacterSet whitespaceCharacterSet] characterIsMember:ch]; } BOOL isNewline(unichar ch) { return [[NSCharacterSet newlineCharacterSet] characterIsMember:ch]; } BOOL isAlpha(unichar ch) { @@ -44,32 +42,6 @@ BOOL isKeyword(unichar ch){ // same as Vim's 'iskeyword' except that Vim's one i }; @implementation NSString (VimHelper) -- (BOOL) isDigit:(NSUInteger)index{ - return isDigit([self characterAtIndex:index]); -} - -- (BOOL) isOctDigit:(NSUInteger)index{ - return isOctDigit([self characterAtIndex:index]); -} - -- (BOOL) isHexDigit:(NSUInteger)index{ - return isHexDigit([self characterAtIndex:index]); -} - -- (BOOL) isAlpha:(NSUInteger)index{ - return isAlpha([self characterAtIndex:index]); -} - -- (BOOL) isDelimeter:(NSUInteger)index{ - return isDelimeter([self characterAtIndex:index]); -} - -- (BOOL) isNewline:(NSUInteger)index{ - return isNewline([self characterAtIndex:index]); } - -- (BOOL) isKeyword:(NSUInteger)index{ - return isKeyword([self characterAtIndex:index]); -} - (NSString*)convertToICURegex:(NSRegularExpressionOptions*)options{ // TODO: These conversion may replace '\\<' into '\\b' @@ -97,6 +69,22 @@ - (NSString*)convertToICURegex:(NSRegularExpressionOptions*)options{ return tmp; } +- (NSMutableString *)newMutableSubstringWithRange:(NSRange)range +{ + NSMutableString *s = [[NSMutableString alloc] initWithCapacity:NSMaxRange(range)]; + unichar buf[1024]; + + while (range.length) { + NSUInteger count = MIN(1024, range.length); + + [self getCharacters:buf range:NSMakeRange(range.location, count)]; + [s appendCharacters:buf length:count]; + range.location += count; + range.length -= count; + } + return s; +} + + (NSString *)stringMadeOfSpaces:(NSUInteger)count { if (count <= 8) { @@ -122,4 +110,9 @@ + (NSMutableString *)mutableStringMadeOfSpaces:(NSUInteger)count return [s autorelease]; } +- (void)appendCharacters:(const unichar *)chars length:(NSUInteger)length +{ + CFStringAppendCharacters((CFMutableStringRef)self, chars, (CFIndex)length); +} + @end diff --git a/XVim/NSTextStorage+VimOperation.h b/XVim/NSTextStorage+VimOperation.h index ba434da4..72aeeb86 100644 --- a/XVim/NSTextStorage+VimOperation.h +++ b/XVim/NSTextStorage+VimOperation.h @@ -11,168 +11,47 @@ #import "XVimDefs.h" #import "XVimTextStoring.h" -typedef enum { - XVimSortOptionReversed = 1, - XVimSortOptionRemoveDuplicateLines = 1 << 1, - XVimSortOptionNumericSort = 1 << 2, - XVimSortOptionIgnoreCase = 1 << 3 -} XVimSortOptions; - /** VimOperation category on NSTextStorage Adds Vim-like functionality to NSTextStorage. **/ - -#pragma mark Term Definitions -/** - * Note that the terms here are not the same definition as Cocoa or Xcode classes uses. - * - * "Character" - * Character is a one unichar value. (any value including tabs,spaces) - * - * "EOF" - * EOF is the position at the end of document(text). - * If we have NSTextView with string "abc" the EOF is just AFTER the 'c'. - * The index of EOF is 3 in this case ( index is 0 based ). - * What we have to think about is a cursor can be on the EOF(when the previous letter is newline) but characterAtIndex: with index of EOF cause an exception. - * We have to be careful about it when calculate and find the position of some motions. - * - * "Newline" - * Newline is defined as "unichar determined by isNewline function". Usually "\n" or "\r". - * - * "Line" - * Line is a sequence of characters terminated by newline or EOF. "Line" includes the last newline character. - * - * "Blankline" - * Blankline is a line which has only newline or EOF. In other words, it is newline character or EOF after newline character. - * - * "Last of Line(LOL)" - * Last of line is the last character of a line EXCLUDING newline character. - * This means that blankline does NOT have an Last of line. - * - * "First of Line(FOL)" - * First of line is the first character of a line excluding newline character. - * This means that blankline does NOT have a First of line. - * - * "First Nonblank of Line" - * First Nonblank of Line is the first printable character in a line. - * - * "End of Line(EOL)" - * End of Line is newline or EOF character at the end of a line. - * A line always has an EOL. - * - * "Beginning of Line (BOL)" - * First character of a line including newline and EOF - * - **/ - -/** - * Line number starts from 1. - * Column number starts from 0. - **/ - -@interface NSTextStorage (VimOperation) +@interface NSTextStorage (VimOperation) #pragma mark Definitions -// Determine if the position specified with "index" is EOF. -- (BOOL) isEOF:(NSUInteger)index; - -// Determine if the position specified with "index" is LOL. -- (BOOL) isLOL:(NSUInteger)index; - -// Determine if the position specified with "index" is EOL. -- (BOOL) isEOL:(NSUInteger)index; - -// Determine if the position is a beginning of line -- (BOOL) isBOL:(NSUInteger)index; - -// Determine if the position specified with "index" is newline. -- (BOOL) isNewline:(NSUInteger)index; - // Determine if the position specified with "index" is white space. - (BOOL) isWhitespace:(NSUInteger)index; -// Determine if the position is on the last line in the document -- (BOOL) isLastLine:(NSUInteger)index; - // Determine if the position is non blank character // EOF is a blank character - (BOOL) isNonblank:(NSUInteger)index; -/** - * Determine if the position specified with "index" is blankline. - * Blankline is one of followings - * - Newline after Newline. Ex. The second '\n' in "abc\n\nabc" is a blankline. First one is not. - * - Newline at begining of the document. - * - EOF after Newline. Ex. The index 4 of "abc\n" is blankline. Note that index 4 is exceed the string length. But the cursor can be there. - * - EOF of 0 sized document. - **/ -- (BOOL) isBlankline:(NSUInteger)index; - -/** - * Determine if the position specified with "index" is valid cursor position in normal mode. - * Valid position is followings - * - Non newline characters. - * - Blankline( including EOF after newline ) - **/ -- (BOOL) isValidCursorPosition:(NSUInteger)index; - #pragma mark Vim operation related methods -- (NSUInteger)prev:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; -- (NSUInteger)next:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt info:(XVimMotionInfo*)info; - -/** - * Returns the position when a cursor goes to upper line. - * @param index the position of the cursor - * @param column the desired position of the column in previous line - * @param count number of repeat - * @param opt currntly nothing is supported - * @return The position to move to. If the current index is on the first line it returns 0 - * - * "column" may be greater then number of characters in the current line. - * Assume that you have following text. - * abcd - * ef - * 12345678 - * When a cursor at character "4" goes up cursor will go at "f". - * When a cursor goes up agein it should got at d. (This is default Vim motion) - * To keep the column position you have to specifi the "column" argument. - * - **/ -- (NSUInteger)prevLine:(NSUInteger)index column:(NSUInteger)column count:(NSUInteger)count option:(MOTION_OPTION)opt; - -/** - * See prevLine's description for meaning of arguments - **/ -- (NSUInteger)nextLine:(NSUInteger)index column:(NSUInteger)column count:(NSUInteger)count option:(MOTION_OPTION)opt; - /** * Returns position of the head of count words forward and an info structure that handles the end of word boundaries. * @param index * @param count - * @param option MOTION_OPTION_NONE or BIGWORD + * @param option MOPT_NONE or MOPT_BIGWORD * @param info This is used with special cases explaind above such as 'cw' or 'w' crossing over the newline. **/ -- (NSUInteger)wordsForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt info:(XVimMotionInfo*)info; +- (NSUInteger)wordsForward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt info:(XVimMotionInfo*)info; -- (NSUInteger)wordsBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; -- (NSUInteger)endOfWordsForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; //e,E -- (NSUInteger)endOfWordsBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; //ge,gE -- (NSUInteger)sentencesForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; -- (NSUInteger)sentencesBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; -- (NSUInteger)paragraphsForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; -- (NSUInteger)paragraphsBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; -- (NSUInteger)nextCharacterInLine:(NSUInteger)index count:(NSUInteger)count character:(unichar)character option:(MOTION_OPTION)opt; -- (NSUInteger)prevCharacterInLine:(NSUInteger)index count:(NSUInteger)count character:(unichar)character option:(MOTION_OPTION)opt; +- (NSUInteger)wordsBackward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt; +- (NSUInteger)endOfWordsForward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt; //e,E +- (NSUInteger)endOfWordsBackward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt; //ge,gE +- (NSUInteger)sentencesForward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt; +- (NSUInteger)sentencesBackward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt; +- (NSUInteger)moveFromIndex:(NSUInteger)index paragraphs:(NSInteger)count option:(XVimMotionOptions)opt; +- (NSUInteger)nextCharacterInLine:(NSUInteger)index count:(NSUInteger)count character:(unichar)character option:(XVimMotionOptions)opt; +- (NSUInteger)prevCharacterInLine:(NSUInteger)index count:(NSUInteger)count character:(unichar)character option:(XVimMotionOptions)opt; // Search starts from 'index+1' to the end of the string -- (NSRange)searchRegexForward:(NSString*)regex from:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; +- (NSRange)searchRegexForward:(NSString*)regex from:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt; // Search starts from 'index-1' to the beginning of the string -- (NSRange)searchRegexBackward:(NSString*)regex from:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; +- (NSRange)searchRegexBackward:(NSString*)regex from:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt; /** @@ -184,36 +63,14 @@ typedef enum { #pragma mark Text Object // TODO: Following code should be rewritten -- (NSRange) currentWord:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt; +- (NSRange) currentWord:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt; // The following code is from xVim by WarWithinMe. // These will be integreted into NSTextView category. // ======================= -// Return the location of the start of indentation on current line. '^' -NSInteger xv_caret(NSString *string, NSInteger index); -// Return the beginning of line location. '0' -NSInteger xv_0(NSString *string, NSInteger index); - // Unlike vim, this function won't ignore indent before the current character // even if what is '{' NSRange xv_current_block(NSString *string, NSUInteger index, NSUInteger repeatCount, BOOL inclusive, char what, char other); NSRange xv_current_quote(NSString *string, NSUInteger index, NSUInteger repeatCount, BOOL inclusive, char what); -// Find char in current line. -// Return the current index if nothing found. -// If inclusive is YES : -// 'fx' returns the index after 'x' -// 'Fx' returns the index before 'x' -NSInteger xv_findChar(NSString *string, NSInteger index, int repeatCount, char command, unichar what, BOOL inclusive); - - - -#pragma mark Conversions -/** - * Returns nearest valid cursor position for normal mode. - * This is usually convert cursor position on newline to previous character since - * a cursor can not be on a newline charaster if its not blankline - **/ -- (NSUInteger)convertToValidCursorPositionForNormalMode:(NSUInteger)index; - @end diff --git a/XVim/NSTextStorage+VimOperation.m b/XVim/NSTextStorage+VimOperation.m index 7e604f72..1e68468f 100644 --- a/XVim/NSTextStorage+VimOperation.m +++ b/XVim/NSTextStorage+VimOperation.m @@ -10,321 +10,16 @@ #import "NSString+VimHelper.h" #import "NSTextStorage+VimOperation.h" #import "Logger.h" +#import "XVimUndo.h" +#import "XVimBuffer.h" @implementation NSTextStorage (VimOperation) -#pragma mark XVimTextStoring Properties - -- (NSString *)xvim_string -{ - return self.string; -} - -- (NSUInteger)xvim_numberOfLines -{ - return [self xvim_lineNumberAtIndex:self.length]; -} - -#pragma mark Settings - -- (NSUInteger)xvim_indentWidth -{ - return 8; -} - -- (NSUInteger)xvim_tabWidth -{ - return 8; -} - -#pragma mark Converting between Indexes and Line Numbers - -// TODO: we may need to keep track line number and position by hooking insertText: method. -// FIXME: this code is actually never called in XVim for XCode, it probably has bugs, it's not tested -- (NSRange)xvim_indexRangeForLineNumber:(NSUInteger)num newLineLength:(NSUInteger *)newLineLength -{ - NSAssert(num > 0, @"line number starts at 1"); - - NSString *string = self.xvim_string; - NSUInteger length = self.length; - NSUInteger lineNum = 0, end = 0, contentsEnd; - - do { - [string getLineStart:NULL end:&end contentsEnd:&contentsEnd forRange:NSMakeRange(end, 0)]; - lineNum++; - if (lineNum == num) { - if (newLineLength) *newLineLength = end - contentsEnd; - return NSMakeRange(end, contentsEnd - end); - } - } while (end < length); - - if (newLineLength) *newLineLength = 0; - - // we have a last empty line after \n - if (contentsEnd < end) { - lineNum++; - if (lineNum == num) { - return NSMakeRange(end, 0); - } - } - - return NSMakeRange(NSNotFound, 0); -} - -// TODO: we may need to keep track line number and position by hooking insertText: method. -// FIXME: this code is actually never called in XVim for XCode, it probably has bugs, it's not tested -- (NSRange)xvim_indexRangeForLines:(NSRange)range -{ - NSString *string = self.xvim_string; - NSUInteger length = self.length, start; - NSUInteger lineNum = 0, end = 0, contentsEnd; - - NSAssert(range.location > 0, @"line number starts at 1"); - - do { - [string getLineStart:NULL end:&end contentsEnd:&contentsEnd forRange:NSMakeRange(end, 0)]; - lineNum++; - if (lineNum == range.location) { - start = end; - } - if (lineNum == NSMaxRange(range)) { - return NSMakeRange(start, end - start); - } - } while (end < length); - - // we have a last empty line after \n - if (contentsEnd < end) { - lineNum++; - if (lineNum == range.location) { - start = end; - } - if (lineNum == NSMaxRange(range)) { - return NSMakeRange(start, end - start); - } - } - - return NSMakeRange(0, length); -} - -- (NSRange)xvim_indexRangeForLineAtIndex:(NSUInteger)index newLineLength:(NSUInteger *)newLineLength -{ - ASSERT_VALID_RANGE_WITH_EOF(index); - NSString *string = self.xvim_string; - NSUInteger len = self.length; - NSUInteger end, contentEnd; - - if (index > len) { - index = len; - } - - [string getLineStart:&index end:&end contentsEnd:&contentEnd forRange:NSMakeRange(index, 0)]; - if (newLineLength) *newLineLength = contentEnd - end; - return NSMakeRange(index, contentEnd - index); -} - -- (NSUInteger)xvim_indexOfLineNumber:(NSUInteger)num -{ - if (num == 1) { - return 0; - } - return [self xvim_indexRangeForLineNumber:num newLineLength:NULL].location; -} - -- (NSUInteger)xvim_lineNumberAtIndex:(NSUInteger)index -{ - ASSERT_VALID_RANGE_WITH_EOF(index); - - NSString *string = self.xvim_string; - NSUInteger len = self.length; - NSUInteger num = 1, pos = 0; - - if (index > len) { - index = len; - } - - do { - num++; - if (index == pos) { - return num; - } - [string getLineStart:NULL end:&pos contentsEnd:NULL forRange:NSMakeRange(pos, 0)]; - } while (pos < index); - - return num; -} - -#pragma mark Converting between Indexes and Line Numbers + Columns - -static NSUInteger xvim_sb_count_columns(xvim_string_buffer_t *sb, NSUInteger tabWidth) -{ - NSUInteger col = 0; - - if (!xvim_sb_at_end(sb)) { - do { - if (xvim_sb_peek(sb) == '\t') { - col += tabWidth; - if (tabWidth) col -= col % tabWidth; - } else { - col++; - } - } while (xvim_sb_next(sb)); - } - - return col; -} - -- (NSUInteger)xvim_columnOfIndex:(NSUInteger)index -{ - NSRange range = [self xvim_indexRangeForLineAtIndex:index newLineLength:NULL]; - xvim_string_buffer_t sb; - - if (index < NSMaxRange(range)) { - range.length = index - range.location; - } - if (range.length == 0) { - return 0; - } - - xvim_sb_init(&sb, self.xvim_string, range.location, range); - return xvim_sb_count_columns(&sb, self.xvim_tabWidth); -} - -- (NSUInteger)xvim_numberOfColumnsInLineAtIndex:(NSUInteger)index -{ - NSRange range = [self xvim_indexRangeForLineAtIndex:index newLineLength:NULL]; - xvim_string_buffer_t sb; - - xvim_sb_init(&sb, self.xvim_string, range.location, range); - return xvim_sb_count_columns(&sb, self.xvim_tabWidth); -} - -- (NSUInteger)xvim_indexOfLineNumber:(NSUInteger)num column:(NSUInteger)column -{ - NSUInteger index = [self xvim_indexOfLineNumber:num]; - - if (column == 0 || index == NSNotFound) { - return index; - } - - NSRange range = [self xvim_indexRangeForLineAtIndex:index newLineLength:NULL]; - NSUInteger tabWidth = self.xvim_tabWidth; - NSUInteger col = 0; - xvim_string_buffer_t sb; - - xvim_sb_init(&sb, self.xvim_string, range.location, range); - do { - if (xvim_sb_peek(&sb) == '\t') { - col += tabWidth; - if (tabWidth) col -= col % tabWidth; - } else { - col++; - } - if (col > column) { - return xvim_sb_index(&sb); - } - } while (xvim_sb_next(&sb) && col < column); - - return xvim_sb_index(&sb); -} - - -#pragma mark Searching particular positions on the current line - -- (NSUInteger)xvim_startOfLine:(NSUInteger)index -{ - ASSERT_VALID_RANGE_WITH_EOF(index); - NSString *string = self.xvim_string; - NSUInteger len = self.length; - - if (index > len) { - index = len; - } - [string getLineStart:&index end:NULL contentsEnd:NULL forRange:NSMakeRange(index, 0)]; - return index; -} - -- (NSUInteger)xvim_firstOfLine:(NSUInteger)index -{ - NSUInteger pos = [self xvim_startOfLine:index]; - - if (pos == index && isNewline([self.xvim_string characterAtIndex:pos])) { - return NSNotFound; - } - return pos; -} - -- (NSUInteger)xvim_endOfLine:(NSUInteger)index -{ - ASSERT_VALID_RANGE_WITH_EOF(index); - NSString *string = self.xvim_string; - NSUInteger len = self.length; - - if (index > len) { - index = len; - } - [string getLineStart:NULL end:NULL contentsEnd:&index forRange:NSMakeRange(index, 0)]; - return index; -} - -- (NSUInteger)xvim_lastOfLine:(NSUInteger)index -{ - NSUInteger pos = [self xvim_endOfLine:index]; - - if (pos <= index && (pos == 0 || isNewline([self.xvim_string characterAtIndex:pos - 1]))) { - return NSNotFound; - } - return pos - 1; -} - -- (NSUInteger)xvim_nextNonblankInLineAtIndex:(NSUInteger)index allowEOL:(BOOL)allowEOL -{ - NSString *s = self.xvim_string; - NSUInteger length = s.length; - xvim_string_buffer_t sb; - unichar c; - - ASSERT_VALID_RANGE_WITH_EOF(index); - - xvim_sb_init(&sb, s, index, NSMakeRange(index, length - index)); - xvim_sb_skip_forward(&sb, [NSCharacterSet whitespaceCharacterSet]); - c = xvim_sb_peek(&sb); - - if (c == XVimInvalidChar || isNewline(c)) { - return allowEOL ? xvim_sb_index(&sb) : NSNotFound; - } - return xvim_sb_index(&sb); -} - -- (NSUInteger)xvim_firstNonblankInLineAtIndex:(NSUInteger)index allowEOL:(BOOL)allowEOL -{ - index = [self xvim_startOfLine:index]; - return [self xvim_nextNonblankInLineAtIndex:index allowEOL:allowEOL]; -} - -- (NSUInteger)xvim_nextDigitInLine:(NSUInteger)index -{ - xvim_string_buffer_t sb; - NSRange range; - - range = [self xvim_indexRangeForLineAtIndex:index newLineLength:NULL]; - xvim_sb_init(&sb, self.xvim_string, index, range); - if (xvim_sb_find_forward(&sb, [NSCharacterSet decimalDigitCharacterSet])) { - return xvim_sb_index(&sb); - } - - return NSNotFound; -} - #pragma mark Definitions - (BOOL) isEOF:(NSUInteger)index{ ASSERT_VALID_RANGE_WITH_EOF(index); - return [[self xvim_string] length] == index; -} - -- (BOOL) isLOL:(NSUInteger)index{ - ASSERT_VALID_RANGE_WITH_EOF(index); - return [self isEOF:index] == NO && [self isNewline:index] == NO && [self isNewline:index+1]; + return self.length == index; } - (BOOL) isEOL:(NSUInteger)index{ @@ -332,38 +27,20 @@ - (BOOL) isEOL:(NSUInteger)index{ return [self isNewline:index] || [self isEOF:index]; } -- (BOOL) isBOL:(NSUInteger)index{ - ASSERT_VALID_RANGE_WITH_EOF(index); - if( 0 == index ){ - return YES; - } - - if( [self isNewline:index-1] ){ - return YES; - } - - return NO; -} - - (BOOL) isNewline:(NSUInteger)index{ ASSERT_VALID_RANGE_WITH_EOF(index); - if( index == [[self xvim_string] length] ){ + if( index == self.length ){ return NO; // EOF is not a newline } - return isNewline([[self xvim_string] characterAtIndex:index]); + return isNewline([self.xvim_buffer.string characterAtIndex:index]); } - (BOOL) isWhitespace:(NSUInteger)index{ ASSERT_VALID_RANGE_WITH_EOF(index); - if( index == [[self xvim_string] length] ){ + if( index == self.length ){ return NO; // EOF is not whitespace } - return isWhitespace([[self xvim_string] characterAtIndex:index]); -} - -- (BOOL) isLastLine:(NSUInteger)index{ - ASSERT_VALID_RANGE_WITH_EOF(index); - return [self xvim_lineNumberAtIndex:index] == [self xvim_numberOfLines]; + return isWhitespace([self.xvim_buffer.string characterAtIndex:index]); } - (BOOL) isNonblank:(NSUInteger)index{ @@ -371,35 +48,30 @@ - (BOOL) isNonblank:(NSUInteger)index{ if( [self isEOF:index]){ return YES; } - return isNonblank([[self xvim_string] characterAtIndex:index]); + return isNonblank([self.xvim_buffer.string characterAtIndex:index]); } + +/** + * Determine if the position specified with "index" is blankline. + * Blankline is one of followings + * - Newline after Newline. Ex. The second '\n' in "abc\n\nabc" is a blankline. First one is not. + * - Newline at begining of the document. + * - EOF after Newline. Ex. The index 4 of "abc\n" is blankline. Note that index 4 is exceed the string length. But the cursor can be there. + * - EOF of 0 sized document. + **/ - (BOOL)isBlankline:(NSUInteger)index { - return [self xvim_indexRangeForLineAtIndex:index newLineLength:NULL].length == 0; + XVimBuffer *buffer = self.xvim_buffer; + return [buffer indexRangeForLineAtIndex:index newLineLength:NULL].length == 0; } - (BOOL) isValidCursorPosition:(NSUInteger)index { ASSERT_VALID_RANGE_WITH_EOF(index); - if( [self isBlankline:index] ){ - return YES; - } - // "index" in not a blankline. - // Then the EOF is not a valid cursor position. - if( [self isEOF:index] ){ - return NO; - } - - // index is never the position of EOF. We can call isNewline with index. - if( ![self isNewline:index]){ - return YES; - } - - return NO; + return [self isBlankline:index] || ![self isEOL:index]; } - #pragma mark Searching Positions /** @@ -409,13 +81,14 @@ - (BOOL) isValidCursorPosition:(NSUInteger)index **/ - (NSUInteger)positionOfMatchedPair:(NSUInteger)pos { - NSString *s = self.xvim_string; + XVimBuffer *buffer = self.xvim_buffer; + NSString *s = buffer.string; // find matching bracketing character and go to it // as long as the nesting level matches up xvim_string_buffer_t sb; - xvim_sb_init(&sb, s, pos, NSMakeRange(pos, [self xvim_endOfLine:pos] - pos)); + xvim_sb_init(&sb, s, pos, pos, [buffer endOfLine:pos]); #define pairs "{}[]()" NSCharacterSet *charset = [NSCharacterSet characterSetWithCharactersInString:@pairs]; @@ -426,7 +99,7 @@ - (NSUInteger)positionOfMatchedPair:(NSUInteger)pos if (xvim_sb_find_forward(&sb, charset)) { start_with_c = xvim_sb_peek(&sb); - xvim_sb_init(&sb, s, xvim_sb_index(&sb), NSMakeRange(0, s.length)); + xvim_sb_init(&sb, s, xvim_sb_index(&sb), 0, s.length); NSUInteger pos = (NSUInteger)(strchr(pairs, start_with_c) - pairs); @@ -456,113 +129,6 @@ - (NSUInteger)positionOfMatchedPair:(NSUInteger)pos } #pragma mark Vim operation related methods -#define charat(x) [[self string] characterAtIndex:(x)] - -- (NSUInteger)prev:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ - if( 0 == index){ - return 0; - } - - NSString* string = [self xvim_string]; - NSUInteger pos = index; - for (NSUInteger i = 0; i < count && pos != 0 ; i++) - { - //Try move to prev position and check if its valid position. - NSUInteger prev = pos-1; //This is the position where we are trying to move to. - // If the position is new line and its not wrapable we stop moving - if( opt == LEFT_RIGHT_NOWRAP && isNewline([[self xvim_string] characterAtIndex:prev]) ){ - break; // not update the position - } - - // If its wrapable, skip newline except its blankline - if (isNewline([string characterAtIndex:prev])) { - if(![self isBlankline:prev]) { - // skip the newline letter at the end of line - prev--; - } - } - - if(charat(prev) == '>' && prev){ - //possible autocomplete glyph that we should skip. - if(charat(prev - 1) == '#'){ - NSUInteger findstart = prev; - while (--findstart ) { - if(charat(findstart) == '#'){ - if(charat(findstart - 1) == '<'){ - prev = findstart - 1; - break; - } - } - } - } - } - - // Now the position can be move to the prev - pos = prev; - } - return pos; -} - -- (NSUInteger)next:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt info:(XVimMotionInfo*)info{ - info->reachedEndOfLine = NO; - - if( index == [[self xvim_string] length] ) - return [[self xvim_string] length]; - - NSString* string = [self xvim_string]; - NSUInteger pos = index; - // If the currenct cursor position is on a newline (blank line) and not wrappable never move the cursor - if( opt == LEFT_RIGHT_NOWRAP && [self isBlankline:pos]){ - return pos; - } - - for (NSUInteger i = 0; i < count && pos < self.length; i++) { - NSUInteger next = pos + 1; - // If the next position is the end of docuement and current position is not a newline - // Never move a cursor to the end of document. - if( [self isEOF:next] && !isNewline([string characterAtIndex:pos]) ){ - info->reachedEndOfLine = YES; - break; - } - - if( opt == LEFT_RIGHT_NOWRAP && isNewline([[self xvim_string] characterAtIndex:next]) ){ - info->reachedEndOfLine = YES; - break; - } - - // If the next position is newline and not a blankline skip it - if (isNewline([string characterAtIndex:next])) { - if(![self isBlankline:next]) { - // skip the newline letter at the end of line - next++; - } - } - pos = next; - } - return pos; -} - -- (NSUInteger)prevLine:(NSUInteger)index column:(NSUInteger)column count:(NSUInteger)count option:(MOTION_OPTION)opt{ - ASSERT_VALID_RANGE_WITH_EOF(index); - - NSUInteger lno = [self xvim_lineNumberAtIndex:index]; - - lno = lno < count ? 1 : lno - count; - return [self xvim_indexOfLineNumber:lno column:column]; -} - -- (NSUInteger)nextLine:(NSUInteger)index column:(NSUInteger)column count:(NSUInteger)count option:(MOTION_OPTION)opt{ - ASSERT_VALID_RANGE_WITH_EOF(index); - - NSUInteger lno = [self xvim_lineNumberAtIndex:index] + count; - NSUInteger lines = self.xvim_numberOfLines; - - if (lno > lines) { - lno = lines; - } - return [self xvim_indexOfLineNumber:lno column:column]; -} - /** From Vim help: word and WORD *word* @@ -584,7 +150,7 @@ - (NSUInteger)nextLine:(NSUInteger)index column:(NSUInteger)column count:(NSUInt next line. **/ -- (NSUInteger)wordsForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt info:(XVimMotionInfo*)info{ +- (NSUInteger)wordsForward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt info:(XVimMotionInfo*)info{ ASSERT_VALID_RANGE_WITH_EOF(index); NSAssert(nil != info, @"Specify info"); @@ -597,10 +163,10 @@ - (NSUInteger)wordsForward:(NSUInteger)index count:(NSUInteger)count option:(MOT return index; } - NSString* str = [self xvim_string]; + NSString* str = self.xvim_buffer.string; unichar lastChar= [str characterAtIndex:index]; BOOL wordInLineFound = NO; - for(NSUInteger i = index+1 ; i <= [[self xvim_string] length]; i++ ){ + for(NSUInteger i = index+1 ; i <= [self length]; i++ ){ // Each time we encounter new word decrement "counter". // Remember blankline is a word unichar curChar; @@ -642,7 +208,7 @@ - (NSUInteger)wordsForward:(NSUInteger)index count:(NSUInteger)count option:(MOT info->lastEndOfWord = i-1; wordInLineFound = NO; }else if(isNonblank(curChar)){ - if(isKeyword(lastChar) != isKeyword(curChar) && opt != BIGWORD){ + if(isKeyword(lastChar) != isKeyword(curChar) && opt != MOPT_BIGWORD){ --count; info->lastEndOfLine = i-1; info->lastEndOfWord = i-1; @@ -675,7 +241,7 @@ - (NSUInteger)wordsForward:(NSUInteger)index count:(NSUInteger)count option:(MOT } lastChar = curChar; - if( isNewline(curChar) && opt == LEFT_RIGHT_NOWRAP ){ + if( isNewline(curChar) && opt == MOPT_NOWRAP ){ pos = i-1; break; } @@ -688,13 +254,13 @@ - (NSUInteger)wordsForward:(NSUInteger)index count:(NSUInteger)count option:(MOT return pos; } -- (NSUInteger)wordsBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ +- (NSUInteger)wordsBackward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt{ ASSERT_VALID_RANGE_WITH_EOF(index); if( 1 >= index ) return 0; NSUInteger indexBoundary = NSNotFound; NSUInteger pos = index-1; - unichar lastChar = [[self xvim_string] characterAtIndex:pos]; + unichar lastChar = [self.xvim_buffer.string characterAtIndex:pos]; // FIXME: This must consider the placeholders // The reason currently commented out is that this method is in NSTextStorage // but the placeholder related codes are in DVTSourceTextView @@ -722,7 +288,7 @@ - (NSUInteger)wordsBackward:(NSUInteger)index count:(NSUInteger)count option:(MO } } */ - unichar curChar = [[self xvim_string] characterAtIndex:i]; + unichar curChar = [self.xvim_buffer.string characterAtIndex:i]; // this branch handles the case that we found a placeholder. // must update the pointer into the string and update the current character found to be at the current index. @@ -733,17 +299,17 @@ - (NSUInteger)wordsBackward:(NSUInteger)index count:(NSUInteger)count option:(MO pos = i; break; } - curChar = [[self xvim_string] characterAtIndex:i]; + curChar = [self.xvim_buffer.string characterAtIndex:i]; } // new word starts between followings.( keyword is determined by 'iskeyword' in Vim ) // - Whitespace(including newline) and Non-Blank - // - keyword and non-keyword(without whitespace) (only when !BIGWORD) - // - non-keyword(without whitespace) and keyword (only when !BIGWORD) + // - keyword and non-keyword(without whitespace) (only when !MOPT_BIGWORD) + // - non-keyword(without whitespace) and keyword (only when !MOPT_BIGWORD) // - newline and newline(blankline) else if( ((isWhitespace(curChar) || isNewline(curChar)) && isNonblank(lastChar)) || - ( opt != BIGWORD && isKeyword(curChar) && !isKeyword(lastChar) && !isWhitespace(lastChar) && !isNewline(lastChar)) || - ( opt != BIGWORD && !isKeyword(curChar) && !isWhitespace(curChar) && !isNewline(lastChar) && isKeyword(lastChar) ) || + ( opt != MOPT_BIGWORD && isKeyword(curChar) && !isKeyword(lastChar) && !isWhitespace(lastChar) && !isNewline(lastChar)) || + ( opt != MOPT_BIGWORD && !isKeyword(curChar) && !isWhitespace(curChar) && !isNewline(lastChar) && isKeyword(lastChar) ) || ( isNewline(curChar) && [self isBlankline:i+1] ) ){ count--; @@ -754,7 +320,7 @@ - (NSUInteger)wordsBackward:(NSUInteger)index count:(NSUInteger)count option:(MO pos = 0; break; } - if( 0 == count || (isNewline(curChar) && opt == LEFT_RIGHT_NOWRAP) ){ + if( 0 == count || (isNewline(curChar) && opt == MOPT_NOWRAP) ){ pos = i+1; break; } @@ -769,7 +335,7 @@ - (NSUInteger)wordsBackward:(NSUInteger)index count:(NSUInteger)count option:(MO * This is because Vim causes an error(beeps) when type "e" at the end of document. * But currently this returns "index" as a next position to move because all evaluators does not expect NSNotFound **/ -- (NSUInteger)endOfWordsForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ +- (NSUInteger)endOfWordsForward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt{ ASSERT_VALID_RANGE_WITH_EOF(index); NSAssert( 0 != count , @"count must be greater than 0"); @@ -780,12 +346,12 @@ - (NSUInteger)endOfWordsForward:(NSUInteger)index count:(NSUInteger)count option } NSUInteger p; - if( opt & MOTION_OPTION_CHANGE_WORD ){ + if( opt & MOPT_CHANGE_WORD ){ p = index; } else { p = index+1; // We start searching end of word from next character } - NSString *string = [self xvim_string]; + NSString *string = self.xvim_buffer.string; while (p < length - 1) { unichar curChar = [string characterAtIndex:p]; unichar nextChar = [string characterAtIndex:p+1]; @@ -793,7 +359,7 @@ - (NSUInteger)endOfWordsForward:(NSUInteger)index count:(NSUInteger)count option // Vim defines "Blank Line as a word" but 'e' does not stop on blank line. // Thats why we are not seeing blank line as a border of a word here (commented out the condition currently) // We may add some option to specify if we count a blank line as a word here. - if( opt & BIGWORD ){ + if( opt & MOPT_BIGWORD ){ if( /*[self isBlankline:p] || */// blank line (isNonblank(curChar) && !isNonblank(nextChar)) // non blank to blank ){ @@ -821,7 +387,7 @@ - (NSUInteger)endOfWordsForward:(NSUInteger)index count:(NSUInteger)count option /** * Returns position of the end of count words backward. **/ -- (NSUInteger)endOfWordsBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ +- (NSUInteger)endOfWordsBackward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt{ // TODO: Implement! return index; } @@ -841,10 +407,10 @@ - (NSUInteger)endOfWordsBackward:(NSUInteger)index count:(NSUInteger)count optio */ // TODO: Treat paragraph and section boundary as sections baundary as well -- (NSUInteger)sentencesForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ //( +- (NSUInteger)sentencesForward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt{ //( NSUInteger pos = index+1; NSUInteger sentence_head = NSNotFound; - NSString* s = [self xvim_string]; + NSString* s = self.xvim_buffer.string; NSUInteger sentence_found = 0; if( pos >= s.length-1 ){ return NSNotFound; @@ -893,7 +459,7 @@ - (NSUInteger)sentencesForward:(NSUInteger)index count:(NSUInteger)count option: return sentence_head; } -- (NSUInteger)sentencesBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ //( +- (NSUInteger)sentencesBackward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt{ //( if( 0 == index ){ return NSNotFound; } @@ -901,7 +467,7 @@ - (NSUInteger)sentencesBackward:(NSUInteger)index count:(NSUInteger)count option NSUInteger pos = index-1; NSUInteger lastSearchBase = index; NSUInteger sentence_head = NSNotFound; - NSString* s = [self xvim_string]; + NSString* s = self.xvim_buffer.string; NSUInteger sentence_found = 0; // Search "." or "!" or "?" backwards and check if it is followed by spaces(and closing characters) for( ; NSNotFound == sentence_head ; pos-- ){ @@ -966,123 +532,72 @@ Note that a blank line (only containing white space) is NOT a paragraph paragraph boundary |posix|. */ -- (NSUInteger)paragraphsForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ //( - NSUInteger pos = index; - NSString* s = [self xvim_string]; - if( 0 == pos ){ - pos = 1; +- (NSUInteger)moveFromIndex:(NSUInteger)index paragraphs:(NSInteger)count option:(XVimMotionOptions)opt +{ + XVimBuffer *buffer = self.xvim_buffer; + NSUInteger length = buffer.length; + NSUInteger nlLen; + BOOL skippingBlankLines = YES, forward; + NSRange range; + + if (count > 0) { + forward = YES; + } else { + forward = NO; + count = -count; } - NSUInteger prevpos = pos - 1; - - NSUInteger paragraph_head = NSNotFound; - int paragraph_found = 0; - BOOL newlines_skipped = NO; - for( ; pos < s.length && NSNotFound == paragraph_head ; pos++,prevpos++ ){ - unichar c = [s characterAtIndex:pos]; - unichar prevc = [s characterAtIndex:prevpos]; - if(isNewline(prevc) && !isNewline(c)){ - if([self xvim_nextNonblankInLineAtIndex:pos allowEOL:NO] == NSNotFound && opt == MOPT_PARA_BOUND_BLANKLINE){ - paragraph_found++; - if(count == paragraph_found){ - paragraph_head = pos; - break; - } + + while (forward ? index < length : index-- > 0) { + BOOL isParaSep = NO; + + range = [buffer indexRangeForLineAtIndex:index newLineLength:&nlLen]; + if (!forward) { + index = range.location; + } + if (range.length == 0) { + isParaSep = YES; + } else if (opt & MOPT_PARA_BOUND_BLANKLINE) { + if ([buffer firstNonblankInLineAtIndex:index allowEOL:NO] == NSNotFound) { + isParaSep = YES; } } - if( (isNewline(c) && isNewline(prevc)) ){ - if( newlines_skipped ){ - paragraph_found++; - if( count == paragraph_found ){ - paragraph_head = pos; - break; - }else{ - newlines_skipped = NO; - } - }else{ - // skip continuous newlines - continue; + if (skippingBlankLines) { + skippingBlankLines = isParaSep; + } else if (isParaSep) { + if (--count == 0) { + return index; } - }else{ - newlines_skipped = YES; + skippingBlankLines = YES; } - } - - if( NSNotFound == paragraph_head ){ - // end of document - paragraph_head = s.length; - while( ![self isValidCursorPosition:paragraph_head] ){ - paragraph_head--; + if (forward) { + index = NSMaxRange(range) + nlLen; } } - - return paragraph_head; -} -- (NSUInteger)paragraphsBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ //( - NSUInteger pos = index; - NSString* s = [self xvim_string]; - if( pos == 0 ){ - return NSNotFound; - } - if( pos == s.length ) - { - pos = pos - 1; - } - NSUInteger prevpos = pos - 1; - NSUInteger paragraph_head = NSNotFound; - int paragraph_found = 0; - BOOL newlines_skipped = NO; - for( ; pos > 0 && NSNotFound == paragraph_head ; pos--,prevpos-- ){ - unichar c = [s characterAtIndex:pos]; - unichar prevc = [s characterAtIndex:prevpos]; - if(isNewline(c) && isNewline(prevc)){ - if( newlines_skipped ){ - paragraph_found++; - if( count == paragraph_found ){ - paragraph_head = pos; - break; - }else{ - newlines_skipped = NO; - } - }else{ - // skip continuous newlines - continue; - } - }else{ - newlines_skipped = YES; - } - } - - if( NSNotFound == paragraph_head ){ - // begining of document - paragraph_head = 0; - } - - return paragraph_head; + return forward ? length : 0; } - -- (NSUInteger)sectionsForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ //( +- (NSUInteger)sectionsForward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt{ //( return 0; } -- (NSUInteger)sectionsBackward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ //( +- (NSUInteger)sectionsBackward:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt{ //( return 0; } -- (NSUInteger)nextCharacterInLine:(NSUInteger)index count:(NSUInteger)count character:(unichar)character option:(MOTION_OPTION)opt{ +- (NSUInteger)nextCharacterInLine:(NSUInteger)index count:(NSUInteger)count character:(unichar)character option:(XVimMotionOptions)opt{ ASSERT_VALID_RANGE_WITH_EOF(index); if( [self isEOF:index] ){ return NSNotFound; } NSUInteger p = index+1; - NSUInteger end = [self xvim_endOfLine:p]; + NSUInteger end = [self.xvim_buffer endOfLine:p]; if( NSNotFound == end ){ return NSNotFound; } for( ; p <= end; p++ ){ - if( [[self xvim_string] characterAtIndex:p] == character ){ + if( [self.xvim_buffer.string characterAtIndex:p] == character ){ count--; if( 0 == count ){ return p; @@ -1092,19 +607,19 @@ - (NSUInteger)nextCharacterInLine:(NSUInteger)index count:(NSUInteger)count char return NSNotFound; } -- (NSUInteger)prevCharacterInLine:(NSUInteger)index count:(NSUInteger)count character:(unichar)character option:(MOTION_OPTION)opt{ +- (NSUInteger)prevCharacterInLine:(NSUInteger)index count:(NSUInteger)count character:(unichar)character option:(XVimMotionOptions)opt{ ASSERT_VALID_RANGE_WITH_EOF(index); if( 0 == index ){ return NSNotFound; } NSUInteger p = index-1; - NSUInteger head = [self xvim_firstOfLine:p]; + NSUInteger head = [self.xvim_buffer firstOfLine:p]; if( NSNotFound == head ){ return NSNotFound; } for( ; p >= head ; p-- ){ - if( [[self xvim_string] characterAtIndex:p] == character ){ + if( [self.xvim_buffer.string characterAtIndex:p] == character ){ count--; if( 0 == count ){ return p; @@ -1114,14 +629,14 @@ - (NSUInteger)prevCharacterInLine:(NSUInteger)index count:(NSUInteger)count char return NSNotFound; } -- (NSRange)searchRegexForward:(NSString*)pattern from:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ +- (NSRange)searchRegexForward:(NSString*)pattern from:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt{ ASSERT_VALID_RANGE_WITH_EOF(index); NSAssert(pattern != nil, @"pattern must not be nil"); NSRange ret = NSMakeRange(NSNotFound,0); NSRegularExpressionOptions options = NSRegularExpressionAnchorsMatchLines; - if( opt & SEARCH_CASEINSENSITIVE ){ + if( opt & MOPT_SEARCH_CASEINSENSITIVE ){ options |= NSRegularExpressionCaseInsensitive; } NSError* err = nil; @@ -1142,8 +657,8 @@ - (NSRange)searchRegexForward:(NSString*)pattern from:(NSUInteger)index count:(N } } - // Then look for the position in range of [BOF,index] if SEARCH_WRAP - if( 0 != count && opt & SEARCH_WRAP ){ + // Then look for the position in range of [BOF,index] if MOPT_SEARCH_WRAP + if( 0 != count && opt & MOPT_SEARCH_WRAP ){ for( NSTextCheckingResult* result in matches ){ if( result.range.location <= index ){ count--; @@ -1159,14 +674,14 @@ - (NSRange)searchRegexForward:(NSString*)pattern from:(NSUInteger)index count:(N return ret; } -- (NSRange)searchRegexBackward:(NSString*)pattern from:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ +- (NSRange)searchRegexBackward:(NSString*)pattern from:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt{ ASSERT_VALID_RANGE_WITH_EOF(index); NSAssert(pattern != nil, @"pattern must not be nil"); NSRange ret = NSMakeRange(NSNotFound,0); NSRegularExpressionOptions options = NSRegularExpressionAnchorsMatchLines; - if( opt & SEARCH_CASEINSENSITIVE ){ + if( opt & MOPT_SEARCH_CASEINSENSITIVE ){ options |= NSRegularExpressionCaseInsensitive; } NSError* err = nil; @@ -1187,8 +702,8 @@ - (NSRange)searchRegexBackward:(NSString*)pattern from:(NSUInteger)index count:( } } - // Then look for the position in range of [index,EOF] if SEARCH_WRAP - if( 0 != count && opt & SEARCH_WRAP ){ + // Then look for the position in range of [index,EOF] if MOPT_SEARCH_WRAP + if( 0 != count && opt & MOPT_SEARCH_WRAP ){ for( NSTextCheckingResult* result in [matches reverseObjectEnumerator] ){ if( result.range.location >= index ){ count--; @@ -1228,12 +743,12 @@ - (NSRange)searchRegexBackward:(NSString*)pattern from:(NSUInteger)index count:( NSInteger index; } NSStringHelper; -void initNSStringHelper(NSStringHelper*, NSString* string, NSUInteger strLen); -void initNSStringHelperBackward(NSStringHelper*, NSString* string, NSUInteger strLen); -unichar characterAtIndex(NSStringHelper*, NSInteger index); +static void initNSStringHelper(NSStringHelper*, NSString* string, NSUInteger strLen); +static void initNSStringHelperBackward(NSStringHelper*, NSString* string, NSUInteger strLen); +static unichar characterAtIndex(NSStringHelper*, NSInteger index); -- (NSRange) currentWord:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt{ - NSString* string = [self xvim_string]; +- (NSRange) currentWord:(NSUInteger)index count:(NSUInteger)count option:(XVimMotionOptions)opt{ + NSString* string = self.xvim_buffer.string; NSInteger maxIndex = self.length - 1; if (index > maxIndex) { return NSMakeRange(NSNotFound, 0); } @@ -1253,7 +768,7 @@ - (NSRange) currentWord:(NSUInteger)index count:(NSUInteger)count option:(MOTION NSCharacterSet *wsSet = [NSCharacterSet whitespaceCharacterSet]; NSCharacterSet *wordSet = nil; - if ( opt & BIGWORD) { + if ( opt & MOPT_BIGWORD) { NSCharacterSet *charSet = [[NSCharacterSet whitespaceAndNewlineCharacterSet] invertedSet]; wordSet = charSet; } @@ -1277,7 +792,7 @@ - (NSRange) currentWord:(NSUInteger)index count:(NSUInteger)count option:(MOTION end = seek_forwards(string, end, searchSet); // For inclusive mode, try to eat some more - if ( !(opt & TEXTOBJECT_INNER)) { + if ( !(opt & MOPT_TEXTOBJECT_INNER)) { NSInteger newEnd = end; if (end < maxIndex) { if (initialCharIsWs) { @@ -1320,22 +835,21 @@ - (NSRange) currentWord:(NSUInteger)index count:(NSUInteger)count option:(MOTION // These will be integreted into NSTextView category. // ========== // NSStringHelper -void initNSStringHelper(NSStringHelper* h, NSString* string, NSUInteger strLen) +static void initNSStringHelper(NSStringHelper* h, NSString* string, NSUInteger strLen) { h->string = string; h->strLen = strLen; h->index = -ITERATE_STRING_BUFFER_SIZE; } -void initNSStringHelperBackward(NSStringHelper* h, NSString* string, NSUInteger strLen) +static void initNSStringHelperBackward(NSStringHelper* h, NSString* string, NSUInteger strLen) { h->string = string; h->strLen = strLen; h->index = strLen; } -NSInteger fetchSubStringFrom(NSStringHelper* h, NSInteger index); -NSInteger fetchSubStringFrom(NSStringHelper* h, NSInteger index) +static NSInteger fetchSubStringFrom(NSStringHelper* h, NSInteger index) { NSInteger copyBegin = index; NSInteger size = (index + ITERATE_STRING_BUFFER_SIZE) > h->strLen ? h->strLen - index : ITERATE_STRING_BUFFER_SIZE; @@ -1343,8 +857,7 @@ NSInteger fetchSubStringFrom(NSStringHelper* h, NSInteger index) return copyBegin; } -NSInteger fetchSubStringEnds(NSStringHelper* h, NSInteger index); -NSInteger fetchSubStringEnds(NSStringHelper* h, NSInteger index) +static NSInteger fetchSubStringEnds(NSStringHelper* h, NSInteger index) { NSInteger copyBegin = (index + 1) >= ITERATE_STRING_BUFFER_SIZE ? index + 1 - ITERATE_STRING_BUFFER_SIZE : 0; NSInteger size = (index + ITERATE_STRING_BUFFER_SIZE) > h->strLen ? h->strLen - index : ITERATE_STRING_BUFFER_SIZE; @@ -1352,7 +865,7 @@ NSInteger fetchSubStringEnds(NSStringHelper* h, NSInteger index) return copyBegin; } -unichar characterAtIndex(NSStringHelper* h, NSInteger index) +static unichar characterAtIndex(NSStringHelper* h, NSInteger index) { if (h->index > index) { @@ -1364,7 +877,7 @@ unichar characterAtIndex(NSStringHelper* h, NSInteger index) return h->buffer[index - h->index]; } -NSInteger xv_caret(NSString *string, NSInteger index) +static NSInteger xv_caret(NSString *string, NSInteger index) { NSInteger resultIndex = index; NSInteger seekingIndex = index; @@ -1393,7 +906,7 @@ NSInteger xv_caret(NSString *string, NSInteger index) return resultIndex; } -NSInteger xv_0(NSString *string, NSInteger index) +static NSInteger xv_0(NSString *string, NSInteger index) { while (index > 0) { @@ -1411,8 +924,7 @@ NSInteger xv_0(NSString *string, NSInteger index) #define MAYBE 2 #define FORWARD 1 #define BACKWARD -1 -int findmatchlimit(NSString* string, NSInteger pos, unichar initc, BOOL cpo_match); -int findmatchlimit(NSString* string, NSInteger pos, unichar initc, BOOL cpo_match) +static int findmatchlimit(NSString* string, NSInteger pos, unichar initc, BOOL cpo_match) { // ---------- unichar findc = 0; // The char to find. @@ -1797,8 +1309,7 @@ static NSInteger seek_forwards(NSString *string, NSInteger end, NSCharacterSet * return searchSet; } -NSInteger find_next_quote(NSString* string, NSInteger start, NSInteger max, unichar quote, BOOL ignoreEscape); -NSInteger find_next_quote(NSString* string, NSInteger start, NSInteger max, unichar quote, BOOL ignoreEscape) +static NSInteger find_next_quote(NSString* string, NSInteger start, NSInteger max, unichar quote, BOOL ignoreEscape) { BOOL ignoreNextChar = NO; @@ -1823,8 +1334,7 @@ NSInteger find_next_quote(NSString* string, NSInteger start, NSInteger max, unic return -1; } -NSInteger find_prev_quote(NSString* string, NSInteger start, unichar quote, BOOL ignoreEscape); -NSInteger find_prev_quote(NSString* string, NSInteger start, unichar quote, BOOL ignoreEscape) +static NSInteger find_prev_quote(NSString* string, NSInteger start, unichar quote, BOOL ignoreEscape) { NSInteger pendingi = -1; NSInteger pendingQuote = -1; @@ -1908,67 +1418,6 @@ NSRange xv_current_quote(NSString *string, NSUInteger index, NSUInteger repeatCo return NSMakeRange(begin, end - begin); } -NSInteger xv_findChar(NSString *string, NSInteger index, int repeatCount, char command, unichar what, BOOL inclusive) -{ - int increment = command <= 'Z' ? -1 : 1; // Capital means backward. - - NSInteger maxIdx = [string length] - 1; - NSInteger idx = index; - NSInteger result = idx; - - NSStringHelper help; - NSStringHelper* h = &help; - - if (increment == -1) - initNSStringHelperBackward(h, string, maxIdx + 1); - else - initNSStringHelper(h, string, maxIdx + 1); - - idx += increment; - while (idx >= 0 && idx <= maxIdx) - { - unichar ch = characterAtIndex(h, idx); - if (ch == what) - { - if ((--repeatCount) == 0) { - result = idx; // Found - break; - } - } else if (isNewline(ch)) - { - break; // Only search in current line. - } - idx += increment; - } - - if (result == idx) - { - if (command == 't') { - --result; - } else if (command == 'T') { - ++result; - } - - if (inclusive && increment == 1) // Include the position we found. - { - ++result; - } - } - - return result; -} #pragma GCC diagnostic pop - -#pragma mark Conversions - -- (NSUInteger)convertToValidCursorPositionForNormalMode:(NSUInteger)index{ - ASSERT_VALID_RANGE_WITH_EOF(index); - // If the current cursor position is not valid for normal mode move it. - if( ![self isValidCursorPosition:index] ){ - return index-1; - } - return index; -} - @end diff --git a/XVim/NSTextView+VimOperation.h b/XVim/NSTextView+VimOperation.h deleted file mode 100644 index f8730739..00000000 --- a/XVim/NSTextView+VimOperation.h +++ /dev/null @@ -1,179 +0,0 @@ -// -// NSTextView+VimOperation.h -// XVim -// -// Created by Suzuki Shuichiro on 8/3/13. -// -// - -// Design Guideline -// -// NSTextView+VimOperation category should have additional medhots to achieve Vim related operations/status. -// This is a top level interface for it and you should not declare something used internally to calculate the position or motion. -// They should go into internal category "VimOperationPrivate" in NSTextView+VimOperation.m. -// The operations declared here must be complete its tasks including edit on characters, positioning a cursor or scrolling. -// If possible methods here should be called WITHOUT spcifing any position or line number as an argument -// because all the operation should be done on current state of the text view. -// But in Vim some operation (in Ex command especiall) takes line number or range as an argument so -// to complete such task you need to decleare some method which takes line number or range as an agument. -// Usually declare one method for one vim operation. -// -// -// Difference between NSTextStorage+VimOperation category -// -// NSTextStorage is a class represents a "Model" of the NSTextView when it is considered as MVC model. -// NSTextStorage holds the content of string and its attributes(color/font...) -// So with VimOperation category NSTextView handles all the view related things too including cursor or scroll. -// In both categories they share the similar name of methods but the role of these methods are different. -// For example, to delete a line both category may have "deleteLine:(NSUInteger)lineNumber" method. -// In NSTextView+VimOperation you must handle where the cursor should go after the operation or -// if it needs to scroll the view. On the other hand in NSTextStorage+VimOperation should provide -// ability to edit its string and do not need to consider any view related thing. -// deleteLine: method in NSTextView+VimOperation would use deleteLine: method in NSTextStorage+VimOperation -// and complete its task. -// -// Public or Private -// A method declared here is a public method to XVimEvaluators. -// As explained above these methods are interface to achieve Vim operation to a NSTextView. -// If you need some helper method used internally, declare it in VimOperationPrivate category in .m file -// -// -// Naming conventoin -// -// All the method in this caregory must have a prefix "xvim_" -// This is to avoid a conflict with method names in NSTextView. - -#import "XVimTextViewProtocol.h" -#import "NSTextStorage+VimOperation.h" -#import - -@interface NSTextView (VimOperation) -// TODO: Method names in category should have prefix like xvim_ -#pragma mark Properties -// Make sure that these property names are not conflicting to the properties in NSTextView -@property(readonly) NSUInteger insertionPoint; -@property(readonly) XVimPosition insertionPosition; -@property(readonly) NSUInteger insertionColumn; -@property(readonly) NSUInteger insertionLine; -@property(readonly) NSUInteger preservedColumn; -@property(readonly) NSUInteger selectionBegin; -@property(readonly) XVimPosition selectionBeginPosition; -@property(readonly) NSUInteger numberOfSelectedLines; -@property(readonly) XVIM_VISUAL_MODE selectionMode; -@property(readonly) BOOL selectionToEOL; -@property(readonly) CURSOR_MODE cursorMode; -@property(readonly) NSURL* documentURL; -@property(strong) id xvimDelegate; -@property BOOL needsUpdateFoundRanges; -@property(readonly) NSArray* foundRanges; -@property(readonly) long long currentLineNumber; - -- (NSString*)xvim_string; -#pragma mark Changing state -- (void)xvim_changeSelectionMode:(XVIM_VISUAL_MODE)mode; -- (void)xvim_escapeFromInsert; -- (void)xvim_setWrapsLines:(BOOL)wraps; - -#pragma mark Operations (Has effect to internal state) -- (void)xvim_adjustCursorPosition; -- (void)xvim_moveToPosition:(XVimPosition)pos; -- (void)xvim_move:(XVimMotion*)motion; -- (void)xvim_selectSwapEndsOnSameLine:(BOOL)onSameLine; -- (void)xvim_delete:(XVimMotion*)motion andYank:(BOOL)yank; -- (void)xvim_change:(XVimMotion*)motion; -- (void)xvim_yank:(XVimMotion*)motion; -- (void)xvim_put:(NSString*)text withType:(TEXT_TYPE)type afterCursor:(bool)after count:(NSUInteger)count; -- (void)xvim_swapCase:(XVimMotion*)motion; -- (void)xvim_makeLowerCase:(XVimMotion*)motion; -- (void)xvim_makeUpperCase:(XVimMotion*)motion; -- (BOOL)xvim_replaceCharacters:(unichar)c count:(NSUInteger)count; -- (void)xvim_join:(NSUInteger)count addSpace:(BOOL)addSpace; -- (void)xvim_filter:(XVimMotion*)motion; -- (void)xvim_shiftRight:(XVimMotion*)motion; -- (void)xvim_shiftLeft:(XVimMotion*)motion; -- (void)xvim_insertText:(NSString*)str line:(NSUInteger)line column:(NSUInteger)column; -- (void)xvim_insertNewlineBelowLine:(NSUInteger)line; -- (void)xvim_insertNewlineBelowCurrentLine; -- (void)xvim_insertNewlineBelowCurrentLineWithIndent; -- (void)xvim_insertNewlineAboveLine:(NSUInteger)line; -- (void)xvim_insertNewlineAboveCurrentLine; -- (void)xvim_insertNewlineAboveCurrentLineWithIndent; -- (void)xvim_insertNewlineAboveAndInsertWithIndent; -- (void)xvim_insertNewlineBelowAndInsertWithIndent; -- (void)xvim_insert:(XVimInsertionPoint)mode blockColumn:(NSUInteger *)column blockLines:(XVimRange *)lines; -- (void)xvim_overwriteCharacter:(unichar)c; -- (BOOL)xvim_incrementNumber:(int64_t)offset; -- (void)xvim_blockInsertFixupWithText:(NSString *)text mode:(XVimInsertionPoint)mode - count:(NSUInteger)count column:(NSUInteger)column lines:(XVimRange)lines; - -/** - * Sort specified lines. - * line1 - line beginning - * line2 - line end - * The lines must be greater than 0 (Line number starts from 1) - * line2 can be less than line1 - * If the range of lines exceeds the maximu line number of the text - * it sorts lines up to end of the text. - * If the range is out of range of current text it does nothing. - **/ -- (void)xvim_sortLinesFrom:(NSUInteger)line1 to:(NSUInteger)line2 withOptions:(XVimSortOptions)options; -- (void)xvim_selectNextPlaceholder; -- (void)xvim_selectPreviousPlaceholder; -- (void)xvim_hideCompletions; - -#pragma mark Scroll -- (NSUInteger)xvim_lineUp:(NSUInteger)index count:(NSUInteger)count; -- (NSUInteger)xvim_lineDown:(NSUInteger)index count:(NSUInteger)count; -- (void)xvim_scroll:(CGFloat)ratio count:(NSUInteger)count; -- (void)xvim_scrollBottom:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb; -- (void)xvim_scrollCenter:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb; -- (void)xvim_scrollTop:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb; -- (void)xvim_scrollTo:(NSUInteger)location; -- (void)xvim_pageForward:(NSUInteger)index count:(NSUInteger)count; -- (void)xvim_pageBackward:(NSUInteger)index count:(NSUInteger)count; -- (void)xvim_halfPageForward:(NSUInteger)index count:(NSUInteger)count; -- (void)xvim_halfPageBackward:(NSUInteger)index count:(NSUInteger)count; -- (void)xvim_scrollPageForward:(NSUInteger)count; -- (void)xvim_scrollPageBackward:(NSUInteger)count; -- (void)xvim_scrollHalfPageForward:(NSUInteger)count; -- (void)xvim_scrollHalfPageBackward:(NSUInteger)count; -- (void)xvim_scrollLineForward:(NSUInteger)count; -- (void)xvim_scrollLineBackward:(NSUInteger)count; - -#pragma mark Search -/** - * Shows the candidate of the next search result. - **/ -- (void)xvim_highlightNextSearchCandidate:(NSString *)regex count:(NSUInteger)count option:(MOTION_OPTION)opt forward:(BOOL)forward; -- (void)xvim_highlightNextSearchCandidateForward:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt; -- (void)xvim_highlightNextSearchCandidateBackward:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt; -- (void)xvim_updateFoundRanges:(NSString*)pattern withOption:(MOTION_OPTION)opt; -- (void)xvim_clearHighlightText; -- (NSRange)xvim_currentWord:(MOTION_OPTION)opt; -- (NSRange)xvim_currentNumber; - -#pragma mark Searching positions -// TODO: Thses method should be internal. Create abstracted interface to achieve the operation uses these methods. -/** - * Takes point in view and returns its index. - * This method automatically convert the "folded index" to "real index" - * When some characters are folded( like placeholders) the pure index for a specifix point is - * less than real index in the string. - **/ -- (NSUInteger)xvim_glyphIndexForPoint:(NSPoint)point; -- (NSRect)xvim_boundingRectForGlyphIndex:(NSUInteger)glyphIndex; - -/** - * Return number of lines in current visible view. - * This means the capacity of the view to show lines and not actually showing now. - * For example the view is 100px height and 1 line is 10px then this returns 10 - * even there are only 2 lines in current view. - * - * TODO: This assumes that all the lines in a view has same text height. - * I thinks this is not bad assumption but there may be a situation the assumption does not work. - **/ -- (NSUInteger)xvim_numberOfLinesInVisibleRect; - -- (void)xvim_syncStateFromView; // update our instance variables with self's properties - -@end diff --git a/XVim/NSTextView+VimOperation.m b/XVim/NSTextView+VimOperation.m deleted file mode 100644 index 821dd9b3..00000000 --- a/XVim/NSTextView+VimOperation.m +++ /dev/null @@ -1,2506 +0,0 @@ -// -// NSTextView+VimOperation.m -// XVim -// -// Created by Suzuki Shuichiro on 8/3/13. -// -// - -#if (XVIM_XCODE_VERSION==5) -#define __XCODE5__ -#endif - -#define __USE_DVTKIT__ - -#ifdef __USE_DVTKIT__ -#import "DVTKit.h" -#import "IDEKit.h" -#endif - -#import "Utils.h" -#import "NSString+VimHelper.h" -#import "NSObject+ExtraData.h" -#import "NSTextView+VimOperation.h" -#import "NSTextStorage+VimOperation.h" -#import "Logger.h" - -#define LOG_STATE() TRACE_LOG(@"mode:%d length:%d cursor:%d ip:%d begin:%d line:%d column:%d preservedColumn:%d", \ - self.selectionMode, \ - [self.textStorage string].length, \ - self.cursorMode, \ - self.insertionPoint, \ - self.selectionBegin, \ - self.insertionLine, \ - self.insertionColumn, \ - self.preservedColumn ) - -// These property declarations for for accessing them as readwrite from inside this category -@interface NSTextView () -@property NSUInteger insertionPoint; -@property XVimPosition insertionPosition; -//@property NSUInteger insertionColumn; // This is readonly also internally -//@property NSUInteger insertionLine; // This is readonly also internally -@property NSUInteger preservedColumn; -@property NSUInteger selectionBegin; -//@property XVimPosition selectionBeginPosition; // This is readonly also internally -@property XVIM_VISUAL_MODE selectionMode; -@property BOOL selectionToEOL; -@property CURSOR_MODE cursorode; -@property(strong) NSURL* documentURL; -@property(readonly) NSMutableArray* foundRanges; - -// Internal properties -@property(strong) NSString* lastYankedText; -@property TEXT_TYPE lastYankedType; -@end - -@interface NSTextView(VimOperationPrivate) -@property BOOL xvim_lockSyncStateFromView; -- (void)xvim_moveCursor:(NSUInteger)pos preserveColumn:(BOOL)preserve; -- (void)xvim_syncState; // update self's properties with our variables -- (NSArray*)xvim_selectedRanges; -- (void)xvim_setSelectedRange:(NSRange)range; -- (XVimRange)xvim_getMotionRange:(NSUInteger)current Motion:(XVimMotion*)motion; -- (NSRange)xvim_getOperationRangeFrom:(NSUInteger)from To:(NSUInteger)to Type:(MOTION_TYPE)type; -- (void)xvim_indentCharacterRange:(NSRange)range; -- (void)xvim_scrollCommon_moveCursorPos:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb; -- (NSUInteger)xvim_lineNumberFromBottom:(NSUInteger)count; -- (NSUInteger)xvim_lineNumberAtMiddle; -- (NSUInteger)xvim_lineNumberFromTop:(NSUInteger)count; -- (NSRange)xvim_search:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt forward:(BOOL)forward; -- (void)xvim_swapCaseForRange:(NSRange)range; -- (void)xvim_registerInsertionPointForUndo; -- (void)xvim_registerPositionForUndo:(NSUInteger)pos; -@end - -@implementation NSTextView (VimOperation) - -#pragma mark internal helpers - -- (void)_xvim_insertSpaces:(NSUInteger)count replacementRange:(NSRange)replacementRange -{ - if (count || replacementRange.length) { - [self insertText:[NSString stringMadeOfSpaces:count] replacementRange:replacementRange]; - } -} - -- (void)_xvim_removeSpacesAtLine:(NSUInteger)line column:(NSUInteger)column count:(NSUInteger)count -{ - NSTextStorage *ts = self.textStorage; - NSUInteger tabWidth = ts.xvim_tabWidth; - NSUInteger pos = [ts xvim_indexOfLineNumber:line column:column]; - NSUInteger end = pos; - NSUInteger width = 0; - NSString *s = self.xvim_string; - - if ([ts isEOL:pos]) { - return; - } - - if ([s characterAtIndex:pos] == '\t') { - NSUInteger col = [ts xvim_columnOfIndex:pos]; - - if (col < column) { - [self _xvim_insertSpaces:tabWidth - (col % tabWidth) replacementRange:NSMakeRange(pos, 1)]; - pos += column - col; - } - } - - while (width < count) { - unichar c = [s characterAtIndex:end]; - - if (c == ' ') { - end++; - width++; - } else if (c == '\t') { - NSUInteger col = column + width; - NSUInteger tw = tabWidth - (col % tabWidth); - - if (width + tw > count) { - [self _xvim_insertSpaces:tw replacementRange:NSMakeRange(end, 1)]; - end += count - width; - width = count; - } else { - end += tw; - width += tw; - } - } else { - break; - } - } - - [self insertText:@"" replacementRange:NSMakeRange(pos, end - pos)]; -} - -- (XVimRange)_xvim_selectedLines{ - if (self.selectionMode == XVIM_VISUAL_NONE) { // its not in selecting mode - return (XVimRange){ NSNotFound, NSNotFound }; - } else { - NSUInteger l1 = [self.textStorage xvim_lineNumberAtIndex:self.insertionPoint]; - NSUInteger l2 = [self.textStorage xvim_lineNumberAtIndex:self.selectionBegin]; - - return (XVimRange){ MIN(l1, l2), MAX(l1, l2) }; - } -} - -- (NSRange)_xvim_selectedRange{ - if (self.selectionMode == XVIM_VISUAL_NONE) { - return NSMakeRange(self.insertionPoint, 0); - } - - if (self.selectionMode == XVIM_VISUAL_CHARACTER) { - XVimRange xvr = XVimMakeRange(self.selectionBegin, self.insertionPoint); - - if (xvr.begin > xvr.end) { - xvr = XVimRangeSwap(xvr); - } - if ([self.textStorage isEOF:xvr.end]) { - xvr.end--; - } - return XVimMakeNSRange(xvr); - } - - if (self.selectionMode == XVIM_VISUAL_LINE) { - XVimRange lines = [self _xvim_selectedLines]; - NSUInteger begin = [self.textStorage xvim_indexOfLineNumber:lines.begin]; - NSUInteger end = [self.textStorage xvim_indexOfLineNumber:lines.end]; - - end = [self.textStorage xvim_endOfLine:end]; - if ([self.textStorage isEOF:end]) { - end--; - } - return NSMakeRange(begin, end - begin + 1); - } - - return NSMakeRange(NSNotFound, 0); -} - -- (XVimSelection)_xvim_selectedBlock{ - XVimSelection result = { }; - - if (self.selectionMode == XVIM_VISUAL_NONE) { - result.top = result.bottom = result.left = result.right = NSNotFound; - return result; - } - - NSTextStorage *ts = self.textStorage; - NSUInteger l1, c11, c12; - NSUInteger l2, c21, c22; - NSUInteger tabWidth = ts.xvim_tabWidth; - NSUInteger pos; - - pos = self.selectionBegin; - l1 = [ts xvim_lineNumberAtIndex:pos]; - c11 = [ts xvim_columnOfIndex:pos]; - if (!tabWidth || [ts isEOF:pos] || [self.xvim_string characterAtIndex:pos] != '\t') { - c12 = c11; - } else { - c12 = c11 + tabWidth - (c11 % tabWidth) - 1; - } - - pos = self.insertionPoint; - l2 = [ts xvim_lineNumberAtIndex:pos]; - c21 = [ts xvim_columnOfIndex:pos]; - if (!tabWidth || [ts isEOF:pos] || [self.xvim_string characterAtIndex:pos] != '\t') { - c22 = c21; - } else { - c22 = c21 + tabWidth - (c21 % tabWidth) - 1; - } - - if (l1 <= l2) { - result.corner |= _XVIM_VISUAL_BOTTOM; - } - if (c11 <= c22) { - result.corner |= _XVIM_VISUAL_RIGHT; - } - result.top = MIN(l1, l2); - result.bottom = MAX(l1, l2); - result.left = MIN(c11, c21); - result.right = MAX(c12, c22); - if (self.selectionToEOL) { - result.right = XVimSelectionEOL; - } - return result; -} - -- (void)__xvim_startYankWithType:(MOTION_TYPE)type -{ - if (self.selectionMode == XVIM_VISUAL_NONE) { - if (type == CHARACTERWISE_EXCLUSIVE || type == CHARACTERWISE_INCLUSIVE) { - self.lastYankedType = TEXT_TYPE_CHARACTERS; - } else if (type == LINEWISE) { - self.lastYankedType = TEXT_TYPE_LINES; - } - } else if (self.selectionMode == XVIM_VISUAL_CHARACTER) { - self.lastYankedType = TEXT_TYPE_CHARACTERS; - } else if (self.selectionMode == XVIM_VISUAL_LINE) { - self.lastYankedType = TEXT_TYPE_LINES; - } else if (self.selectionMode == XVIM_VISUAL_BLOCK) { - self.lastYankedType = TEXT_TYPE_BLOCK; - } - TRACE_LOG(@"YANKED START WITH TYPE:%d", self.lastYankedType); -} - -- (void)_xvim_yankRange:(NSRange)range withType:(MOTION_TYPE)type -{ - NSString *s; - BOOL needsNL; - - [self __xvim_startYankWithType:type]; - - needsNL = self.lastYankedType == TEXT_TYPE_LINES; - if (range.length) { - s = [self.xvim_string substringWithRange:range]; - if (needsNL && !isNewline([s characterAtIndex:s.length - 1])) { - s = [s stringByAppendingString:@"\n"]; - } - } else if (needsNL) { - s = @"\n"; - } else { - s = @""; - } - - self.lastYankedText = s; - TRACE_LOG(@"YANKED STRING : %@", s); -} - -- (void)_xvim_yankSelection:(XVimSelection)sel -{ - NSTextStorage *ts = self.textStorage; - NSString *s = self.xvim_string; - NSUInteger tabWidth = ts.xvim_tabWidth; - - NSMutableString *ybuf = [[NSMutableString alloc] init]; - self.lastYankedType = TEXT_TYPE_BLOCK; - - for (NSUInteger line = sel.top; line <= sel.bottom; line++) { - NSUInteger lpos = [ts xvim_indexOfLineNumber:line column:sel.left]; - NSUInteger rpos = [ts xvim_indexOfLineNumber:line column:sel.right]; - - /* if lpos points in the middle of a tab, split it and advance lpos */ - if (![ts isEOF:lpos] && [s characterAtIndex:lpos] == '\t') { - NSUInteger lcol = sel.left - (sel.left % tabWidth); - - if (lcol < sel.left) { - TRACE_LOG("lcol %ld left %ld tab %ld", (long)lcol, (long)sel.left, (long)tabWidth); - NSUInteger count = tabWidth - (sel.left - lcol); - - if (lpos == rpos) { - /* if rpos points to the same tab, truncate it to the right also */ - count = sel.right - sel.left + 1; - } - [ybuf appendString:[NSString stringMadeOfSpaces:count]]; - lpos++; - } - } - - if (lpos <= rpos) { - if (sel.right == XVimSelectionEOL) { - [ybuf appendString:[s substringWithRange:NSMakeRange(lpos, rpos - lpos)]]; - } else { - NSRange r = NSMakeRange(lpos, rpos - lpos + 1); - NSUInteger rcol; - BOOL mustPad = NO; - - if ([ts isEOF:rpos]) { - rcol = [ts xvim_columnOfIndex:rpos]; - mustPad = YES; - r.length--; - } else { - unichar c = [s characterAtIndex:rpos]; - if (isNewline(c)) { - rcol = [ts xvim_columnOfIndex:rpos]; - mustPad = YES; - r.length--; - } else if (c == '\t') { - rcol = [ts xvim_columnOfIndex:rpos]; - if (sel.right - rcol + 1 < tabWidth) { - mustPad = YES; - r.length--; - } - } - } - - if (r.length) { - [ybuf appendString:[s substringWithRange:r]]; - } - - if (mustPad) { - [ybuf appendString:[NSString stringMadeOfSpaces:sel.right - rcol + 1]]; - } - } - } - [ybuf appendString:@"\n"]; - } - - self.lastYankedText = ybuf; - TRACE_LOG(@"YANKED STRING : %@", ybuf); - [ybuf release]; -} - -- (void)_xvim_killSelection:(XVimSelection)sel -{ - NSTextStorage *ts = self.textStorage; - NSString *s = self.xvim_string; - NSUInteger tabWidth = ts.xvim_tabWidth; - - for (NSUInteger line = sel.bottom; line >= sel.top; line--) { - NSUInteger lpos = [ts xvim_indexOfLineNumber:line column:sel.left]; - NSUInteger rpos = [ts xvim_indexOfLineNumber:line column:sel.right]; - NSUInteger nspaces = 0; - - if ([ts isEOF:lpos]) { - continue; - } - - if ([s characterAtIndex:lpos] == '\t') { - NSUInteger lcol = [ts xvim_columnOfIndex:lpos]; - - if (lcol < sel.left) { - nspaces = sel.left - lcol; - if (lpos == rpos) { - nspaces = tabWidth - (sel.right - sel.left + 1); - } - } - } - - if ([ts isEOL:rpos]) { - rpos--; - } else if (lpos < rpos) { - if ([s characterAtIndex:rpos] == '\t') { - nspaces += tabWidth - (sel.right - [ts xvim_columnOfIndex:rpos] + 1); - } - } - - NSRange range = NSMakeRange(lpos, rpos - lpos + 1); - NSString *repl = @""; - - if (nspaces) { - repl = [NSString stringWithFormat:@"%*s", (int)nspaces, ""]; - } - [self insertText:repl replacementRange:range]; - } -} - - -#pragma mark Properties - -/** - * Properties in this category uses NSObject+ExtraData to - * store additional properties. - **/ - -- (NSUInteger)insertionPoint{ - id ret = [self dataForName:@"insertionPoint"]; - return nil == ret ? 0 : [ret unsignedIntegerValue]; -} - -- (void)setInsertionPoint:(NSUInteger)insertion{ - [self setUnsignedInteger:insertion forName:@"insertionPoint"]; -} - -- (XVimPosition)insertionPosition{ - return XVimMakePosition(self.insertionLine, self.insertionColumn); -} - -- (void)setInsertionPosition:(XVimPosition)pos{ - // Not implemented yet (Just update corresponding insertionPoint) -} - -- (NSUInteger)insertionColumn{ - return [self.textStorage xvim_columnOfIndex:self.insertionPoint]; -} - -- (NSUInteger)insertionLine{ - return [self.textStorage xvim_lineNumberAtIndex:self.insertionPoint]; -} - -- (NSUInteger)preservedColumn{ - id ret = [self dataForName:@"preservedColumn"]; - return nil == ret ? 0 : [ret unsignedIntegerValue]; -} - -- (void)setPreservedColumn:(NSUInteger)preservedColumn{ - TRACE_LOG(@"%d" , preservedColumn); - [self setUnsignedInteger:preservedColumn forName:@"preservedColumn"]; -} - -- (NSUInteger)selectionBegin{ - id ret = [self dataForName:@"selectionBegin"]; - return nil == ret ? 0 : [ret unsignedIntegerValue]; -} - -- (void)setSelectionBegin:(NSUInteger)selectionBegin{ - [self setUnsignedInteger:selectionBegin forName:@"selectionBegin"]; -} - -- (XVimPosition)selectionBeginPosition{ - return XVimMakePosition([self.textStorage xvim_lineNumberAtIndex:self.selectionBegin], [self.textStorage xvim_columnOfIndex:self.selectionBegin]); -} - -- (NSUInteger)numberOfSelectedLines{ - if (XVIM_VISUAL_NONE == self.selectionMode) { - return 0; - } - - XVimRange lines = [self _xvim_selectedLines]; - return lines.end - lines.begin + 1; -} - -- (BOOL)selectionToEOL{ - return [[self dataForName:@"selectionToEOL"] boolValue]; -} - -- (void)setSelectionToEOL:(BOOL)selectionToEOL{ - [self setBool:selectionToEOL forName:@"selectionToEOL"]; -} - -- (XVIM_VISUAL_MODE) selectionMode{ - id ret = [self dataForName:@"selectionMode"]; - return nil == ret ? XVIM_VISUAL_NONE : (XVIM_VISUAL_MODE)[ret integerValue]; -} - -- (void)setSelectionMode:(XVIM_VISUAL_MODE)selectionMode{ - if (self.selectionMode != selectionMode) { - self.selectionToEOL = NO; - [self setInteger:selectionMode forName:@"selectionMode"]; - } -} - -- (CURSOR_MODE) cursorMode{ - id ret = [self dataForName:@"cursorMode"]; - return nil == ret ? CURSOR_MODE_COMMAND : (CURSOR_MODE)[ret integerValue]; -} - -- (void)setCursorMode:(CURSOR_MODE)cursorMode{ - [self setInteger:cursorMode forName:@"cursorMode"]; -} - -- (NSURL*)documentURL{ -#ifdef __USE_DVTKIT__ - if( [self.delegate isKindOfClass:[IDEEditor class]] ){ - return [(IDEEditorDocument*)((IDEEditor*)self.delegate).document fileURL]; - }else{ - return nil; - } -#else - return nil; -#endif -} - -- (void)setXvimDelegate:(id)xvimDelegate{ - [self setData:xvimDelegate forName:@"xvimDelegate"]; -} - -- (id)xvimDelegate{ - return [self dataForName:@"xvimDelegate"]; -} - -- (BOOL)needsUpdateFoundRanges{ - id ret = [self dataForName:@"needsUpdateFoundRanges"]; - return nil == ret ? NO : [ret boolValue]; -} - -- (void)setNeedsUpdateFoundRanges:(BOOL)needsUpdateFoundRanges{ - [self setBool:needsUpdateFoundRanges forName:@"needsUpdateFoundRanges"]; -} - -- (NSMutableArray*)foundRanges{ - id ranges = [self dataForName:@"foundRanges"]; - if( nil == ranges ){ - ranges = [[[NSMutableArray alloc] init] autorelease]; - [self setData:ranges forName:@"foundRanges"]; - } - return ranges; -} - -#pragma mark Internal properties - -- (NSString*) lastYankedText{ - return [self dataForName:@"lastYankedText"]; -} - -- (void)setLastYankedText:(NSString*)text{ - [self setData:[NSString stringWithString:text] forName:@"lastYankedText"]; -} - -- (TEXT_TYPE) lastYankedType{ - return (TEXT_TYPE)[[self dataForName:@"lastYankedType"] integerValue]; -} - -- (void) setLastYankedType:(TEXT_TYPE)type{ - [self setInteger:type forName:@"lastYankedType"]; -} - -- (long long)currentLineNumber { -#ifdef __USE_DVTKIT__ - if( [self isKindOfClass:[DVTSourceTextView class]] ){ - return [(DVTSourceTextView*)self _currentLineNumber]; - } -#else -#error You must implement here. -#endif - NSAssert(NO, @"You must implement here if you do not use this with DVTSourceTextView"); - return -1; -} - - -- (NSString*)xvim_string{ - return [self.textStorage xvim_string]; -} - -#pragma mark Status - -- (NSUInteger)xvim_numberOfLinesInVisibleRect{ - NSScrollView *scrollView = [self enclosingScrollView]; - NSTextContainer *container = [self textContainer]; - NSRect glyphRect = [[self layoutManager] boundingRectForGlyphRange:[self selectedRange] inTextContainer:container]; - NSAssert( glyphRect.size.height != 0 , @"Need to fix the code here if the height of current selected character can be 0 here" ); - return [scrollView contentView].bounds.size.height / glyphRect.size.height; -} - - - -#pragma mark Changing state - - -- (void)xvim_changeSelectionMode:(XVIM_VISUAL_MODE)mode{ - if( self.selectionMode == XVIM_VISUAL_NONE && mode != XVIM_VISUAL_NONE ){ - self.selectionBegin = self.insertionPoint; - }else if( self.selectionMode != XVIM_VISUAL_NONE && mode == XVIM_VISUAL_NONE){ - self.selectionBegin = NSNotFound; - } - self.selectionMode = mode; - [self xvim_syncState]; - return; -} - -- (void)xvim_escapeFromInsert{ - if( self.cursorMode == CURSOR_MODE_INSERT ){ - self.cursorMode = CURSOR_MODE_COMMAND; - if(![self.textStorage isBOL:self.insertionPoint]){ - [self xvim_moveCursor:self.insertionPoint-1 preserveColumn:NO]; - } - [self xvim_syncState]; - } -} - -- (void)xvim_setWrapsLines:(BOOL)wraps { -#ifdef __USE_DVTKIT__ - if( [self isKindOfClass:[DVTSourceTextView class]]){ - [(DVTSourceTextView*)self setWrapsLines:wraps]; - } -#endif -} - -#pragma mark Operations -/** - * Adjust cursor position if the position is not valid as normal mode cursor position - * This method may changes selected range of the view. - **/ -- (void)xvim_adjustCursorPosition{ - // If the current cursor position is not valid for normal mode move it. - if( ![self.textStorage isValidCursorPosition:[self selectedRange].location] ){ - NSRange currentRange = [self selectedRange]; - [self xvim_selectPreviousPlaceholder]; - NSRange prevPlaceHolder = [self selectedRange]; - if( currentRange.location != prevPlaceHolder.location && currentRange.location == (prevPlaceHolder.location + prevPlaceHolder.length) ){ - //The condition here means that just before current insertion point is a placeholder. - //So we select the the place holder and its already selected by "selectedPreviousPlaceholder" above - }else{ - [self setSelectedRange:NSMakeRange(currentRange.location-1, 0)]; - } - } - return; -} - -- (void)xvim_moveToPosition:(XVimPosition)pos{ - [self xvim_moveCursor:[self.textStorage xvim_indexOfLineNumber:pos.line column:pos.column] preserveColumn:NO]; - [self xvim_syncState]; -} - -- (void)xvim_move:(XVimMotion*)motion{ - XVimRange r = [self xvim_getMotionRange:self.insertionPoint Motion:motion]; - if( r.end == NSNotFound ){ - return; - } - - if( self.selectionMode != XVIM_VISUAL_NONE && [motion isTextObject]){ - if( self.selectionMode == XVIM_VISUAL_LINE){ - // Motion with text object in VISUAL LINE changes visual mode to VISUAL CHARACTER - [self setSelectionMode:XVIM_VISUAL_CHARACTER]; - } - - if( self.insertionPoint < self.selectionBegin ){ - // When insertionPoint < selectionBegin it only changes insertion point to begining of the text object - [self xvim_moveCursor:r.begin preserveColumn:NO]; - }else{ - // Text object expands one text object ( the text object under insertion point + 1 ) - if( ![self.textStorage isEOF:self.insertionPoint+1]){ - r = [self xvim_getMotionRange:self.insertionPoint+1 Motion:motion]; - } - if( self.selectionBegin > r.begin ){ - self.selectionBegin = r.begin; - } - [self xvim_moveCursor:r.end preserveColumn:NO]; - } - } else { - switch( motion.motion ){ - case MOTION_LINE_BACKWARD: - case MOTION_LINE_FORWARD: - case MOTION_LASTLINE: - case MOTION_LINENUMBER: - // TODO: Preserve column option can be included in motion object - if (self.selectionMode == XVIM_VISUAL_BLOCK && self.selectionToEOL) { - r.end = [self.textStorage xvim_endOfLine:r.end]; - } - [self xvim_moveCursor:r.end preserveColumn:YES]; - break; - case MOTION_END_OF_LINE: - self.selectionToEOL = YES; - [self xvim_moveCursor:r.end preserveColumn:NO]; - break; - - default: - self.selectionToEOL = NO; - [self xvim_moveCursor:r.end preserveColumn:NO]; - break; - } - } - [self setNeedsDisplay:YES]; - [self xvim_syncState]; -} - -- (void)xvim_selectSwapEndsOnSameLine:(BOOL)onSameLine{ - if (self.selectionMode == XVIM_VISUAL_BLOCK) { - XVimPosition start, end; - XVimSelection sel; - NSUInteger pos; - - self.selectionToEOL = NO; - sel = [self _xvim_selectedBlock]; - if (onSameLine) { - sel.corner ^= _XVIM_VISUAL_RIGHT; - } else { - sel.corner ^= _XVIM_VISUAL_RIGHT | _XVIM_VISUAL_BOTTOM; - } - - if (sel.corner & _XVIM_VISUAL_BOTTOM) { - start.line = sel.top; - end.line = sel.bottom; - } else { - end.line = sel.top; - start.line = sel.bottom; - } - - if (sel.corner & _XVIM_VISUAL_RIGHT) { - start.column = sel.left; - end.column = sel.right; - } else { - end.column = sel.left; - start.column = sel.right; - } - - pos = [self.textStorage xvim_indexOfLineNumber:start.line column:start.column]; - self.selectionBegin = pos; - pos = [self.textStorage xvim_indexOfLineNumber:end.line column:end.column]; - [self xvim_moveCursor:pos preserveColumn:NO]; - } else if (self.selectionMode != XVIM_VISUAL_NONE) { - NSUInteger begin = self.selectionBegin; - - self.selectionBegin = self.insertionPoint; - [self xvim_moveCursor:begin preserveColumn:NO]; - [self setNeedsDisplay:YES]; - } - [self xvim_syncState]; -} - -- (void)xvim_delete:(XVimMotion*)motion andYank:(BOOL)yank -{ - NSAssert( !(self.selectionMode == XVIM_VISUAL_NONE && motion == nil), - @"motion must be specified if current selection mode is not visual"); - if (self.insertionPoint == 0 && [[self xvim_string] length] == 0) { - return ; - } - NSUInteger newPos = NSNotFound; - - [self xvim_registerInsertionPointForUndo]; - - motion.info->deleteLastLine = NO; - if (self.selectionMode == XVIM_VISUAL_NONE) { - NSRange r; - XVimRange motionRange = [self xvim_getMotionRange:self.insertionPoint Motion:motion]; - if( motionRange.end == NSNotFound ){ - return; - } - // We have to treat some special cases - // When a cursor get end of line with "l" motion, make the motion type to inclusive. - // This make you to delete the last character. (if its exclusive last character never deleted with "dl") - if( motion.motion == MOTION_FORWARD && motion.info->reachedEndOfLine ){ - if( motion.type == CHARACTERWISE_EXCLUSIVE ){ - motion.type = CHARACTERWISE_INCLUSIVE; - }else if( motion.type == CHARACTERWISE_INCLUSIVE ){ - motion.type = CHARACTERWISE_EXCLUSIVE; - } - } - if( motion.motion == MOTION_WORD_FORWARD ){ - if ( (motion.info->isFirstWordInLine && motion.info->lastEndOfLine != NSNotFound )) { - // Special cases for word move over a line break. - motionRange.end = motion.info->lastEndOfLine; - motion.type = CHARACTERWISE_INCLUSIVE; - } - else if( motion.info->reachedEndOfLine ){ - if( motion.type == CHARACTERWISE_EXCLUSIVE ){ - motion.type = CHARACTERWISE_INCLUSIVE; - }else if( motion.type == CHARACTERWISE_INCLUSIVE ){ - motion.type = CHARACTERWISE_EXCLUSIVE; - } - } - } - r = [self xvim_getOperationRangeFrom:motionRange.begin To:motionRange.end Type:motion.type]; - if( motion.type == LINEWISE && [self.textStorage isLastLine:motionRange.end]){ - if( r.location != 0 ){ - motion.info->deleteLastLine = YES; - r.location--; - r.length++; - } - } - if (yank) { - [self _xvim_yankRange:r withType:motion.type]; - } - [self insertText:@"" replacementRange:r]; - if (motion.type == LINEWISE) { - newPos = [self.textStorage xvim_firstNonblankInLineAtIndex:self.insertionPoint allowEOL:YES]; - } - } else if (self.selectionMode != XVIM_VISUAL_BLOCK) { - BOOL toFirstNonBlank = (self.selectionMode == XVIM_VISUAL_LINE); - NSRange range = [self _xvim_selectedRange]; - - // Currently not supportin deleting EOF with selection mode. - // This is because of the fact that NSTextView does not allow select EOF - - if (yank) { - [self _xvim_yankRange:range withType:DEFAULT_MOTION_TYPE]; - } - [self insertText:@"" replacementRange:range]; - if (toFirstNonBlank) { - newPos = [self.textStorage xvim_firstNonblankInLineAtIndex:range.location allowEOL:YES]; - } else { - newPos = range.location; - } - } else { - XVimSelection sel = [self _xvim_selectedBlock]; - if (yank) { - [self _xvim_yankSelection:sel]; - } - [self _xvim_killSelection:sel]; - - newPos = [self.textStorage xvim_indexOfLineNumber:sel.top column:sel.left]; - } - - [self.xvimDelegate textView:self didDelete:self.lastYankedText withType:self.lastYankedType]; - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; - if (newPos != NSNotFound) { - [self xvim_moveCursor:newPos preserveColumn:NO]; - } -} - -- (void)xvim_change:(XVimMotion*)motion{ - // We do not need to call this since this method uses xvim_delete to operate on text - //[self xvim_registerInsertionPointForUndo]; - - BOOL insertNewline = NO; - if( motion.type == LINEWISE || self.selectionMode == XVIM_VISUAL_LINE){ - // 'cc' deletes the lines but need to keep the last newline. - // So insertNewline as 'O' does before entering insert mode - insertNewline = YES; - } - - // "cw" is like "ce" if the cursor is on a word ( in this case blank line is not treated as a word ) - if( motion.motion == MOTION_WORD_FORWARD && [self.textStorage isNonblank:self.insertionPoint] ){ - motion.motion = MOTION_END_OF_WORD_FORWARD; - motion.type = CHARACTERWISE_INCLUSIVE; - motion.option |= MOTION_OPTION_CHANGE_WORD; - } - self.cursorMode = CURSOR_MODE_INSERT; - [self xvim_delete:motion andYank:YES]; - if( motion.info->deleteLastLine){ - [self xvim_insertNewlineAboveLine:[self.textStorage xvim_lineNumberAtIndex:self.insertionPoint]]; - } - else if( insertNewline ){ - [self xvim_insertNewlineAboveLine:[self.textStorage xvim_lineNumberAtIndex:self.insertionPoint]]; - }else{ - - } - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; - [self xvim_syncState]; -} - -- (void)xvim_yank:(XVimMotion*)motion{ - NSAssert( !(self.selectionMode == XVIM_VISUAL_NONE && motion == nil), @"motion must be specified if current selection mode is not visual"); - NSUInteger newPos = NSNotFound; - - if( self.selectionMode == XVIM_VISUAL_NONE ){ - NSRange r; - XVimRange to = [self xvim_getMotionRange:self.insertionPoint Motion:motion]; - if( NSNotFound == to.end ){ - return; - } - // We have to treat some special cases (same as delete) - if( motion.motion == MOTION_FORWARD && motion.info->reachedEndOfLine){ - motion.type = CHARACTERWISE_INCLUSIVE; - } - if( motion.motion == MOTION_WORD_FORWARD ){ - if ( (motion.info->isFirstWordInLine && motion.info->lastEndOfLine != NSNotFound )) { - // Special cases for word move over a line break. - to.end = motion.info->lastEndOfLine; - motion.type = CHARACTERWISE_INCLUSIVE; - } - else if( motion.info->reachedEndOfLine ){ - if( motion.type == CHARACTERWISE_EXCLUSIVE ){ - motion.type = CHARACTERWISE_INCLUSIVE; - }else if( motion.type == CHARACTERWISE_INCLUSIVE ){ - motion.type = CHARACTERWISE_EXCLUSIVE; - } - } - } - r = [self xvim_getOperationRangeFrom:to.begin To:to.end Type:motion.type]; - BOOL eof = [self.textStorage isEOF:to.end]; - BOOL blank = [self.textStorage isBlankline:to.end]; - if( motion.type == LINEWISE && blank && eof){ - if( r.location != 0 ){ - r.location--; - r.length++; - } - } - [self _xvim_yankRange:r withType:motion.type]; - } else if (self.selectionMode != XVIM_VISUAL_BLOCK) { - NSRange range = [self _xvim_selectedRange]; - - newPos = range.location; - [self _xvim_yankRange:range withType:DEFAULT_MOTION_TYPE]; - } else { - XVimSelection sel = [self _xvim_selectedBlock]; - - newPos = [self.textStorage xvim_indexOfLineNumber:sel.top column:sel.left]; - [self _xvim_yankSelection:sel]; - } - - [self.xvimDelegate textView:self didYank:self.lastYankedText withType:self.lastYankedType]; - if (newPos != NSNotFound) { - [self xvim_moveCursor:newPos preserveColumn:NO]; - } - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; -} - -- (void)xvim_put:(NSString*)text withType:(TEXT_TYPE)type afterCursor:(bool)after count:(NSUInteger)count{ - [self xvim_registerInsertionPointForUndo]; - - TRACE_LOG(@"text:%@ type:%d afterCursor:%d count:%d", text, type, after, count); - if( self.selectionMode != XVIM_VISUAL_NONE ){ - // FIXME: Make them not to change text from register... - text = [NSString stringWithString:text]; // copy string because the text may be changed with folloing delete if it is from the same register... - [self xvim_delete:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, 1) andYank:YES]; - after = NO; - } - - NSUInteger insertionPointAfterPut = self.insertionPoint; - NSUInteger targetPos = self.insertionPoint; - if( type == TEXT_TYPE_CHARACTERS ){ - //Forward insertion point +1 if after flag if on - if( 0 != text.length ){ - if (![self.textStorage isNewline:self.insertionPoint] && after) { - targetPos++; - } - insertionPointAfterPut = targetPos; - for(NSUInteger i = 0; i < count ; i++ ){ - [self insertText:text replacementRange:NSMakeRange(targetPos,0)]; - } - insertionPointAfterPut += text.length*count - 1; - } - }else if( type == TEXT_TYPE_LINES ){ - if( after ){ - [self xvim_insertNewlineBelowCurrentLine]; - targetPos = self.insertionPoint; - }else{ - targetPos= [self.textStorage xvim_startOfLine:self.insertionPoint]; - } - insertionPointAfterPut = targetPos; - for(NSUInteger i = 0; i < count ; i++ ){ - if( after && i == 0 ){ - // delete newline at the end. (TEXT_TYPE_LINES always have newline at the end of the text) - NSString* t = [text substringToIndex:text.length-1]; - [self insertText:t replacementRange:NSMakeRange(targetPos,0)]; - } else{ - [self insertText:text replacementRange:NSMakeRange(targetPos,0)]; - } - } - }else if( type == TEXT_TYPE_BLOCK ){ - //Forward insertion point +1 if after flag if on - if (![self.textStorage isNewline:self.insertionPoint] && ![self.textStorage isEOF:self.insertionPoint] && after) { - self.insertionPoint++; - } - insertionPointAfterPut = self.insertionPoint; - NSUInteger insertPos = self.insertionPoint; - NSUInteger column = [self.textStorage xvim_columnOfIndex:insertPos]; - NSUInteger startLine = [self.textStorage xvim_lineNumberAtIndex:insertPos]; - NSArray* lines = [text componentsSeparatedByString:@"\n"]; - for( NSUInteger i = 0 ; i < lines.count ; i++){ - NSString* line = [lines objectAtIndex:i]; - NSUInteger targetLine = startLine + i; - NSUInteger head = [self.textStorage xvim_indexOfLineNumber:targetLine]; - if( NSNotFound == head ){ - NSAssert( targetLine != 0, @"This should not be happen"); - [self xvim_insertNewlineBelowLine:targetLine-1]; - head = [self.textStorage xvim_indexOfLineNumber:targetLine]; - } - NSAssert( NSNotFound != head , @"Head of the target line must be found at this point"); - - // Find next insertion point - NSUInteger max = [self.textStorage xvim_numberOfColumnsInLineAtIndex:head]; - NSAssert( max != NSNotFound , @"Should not be NSNotFound"); - if( column > max ){ - // If the line does not have enough column pad it with spaces - NSUInteger end = [self.textStorage xvim_endOfLine:head]; - - [self _xvim_insertSpaces:column - max replacementRange:NSMakeRange(end, 0)]; - } - for(NSUInteger i = 0; i < count ; i++ ){ - [self xvim_insertText:line line:targetLine column:column]; - } - } - } - - - [self xvim_moveCursor:insertionPointAfterPut preserveColumn:NO]; - [self xvim_syncState]; - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; -} - -- (void)xvim_swapCase:(XVimMotion*)motion{ - if( self.insertionPoint == 0 && [[self xvim_string] length] == 0 ){ - return ; - } - - if( self.selectionMode == XVIM_VISUAL_NONE ){ - if( motion.motion == MOTION_NONE ){ - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_FORWARD,CHARACTERWISE_EXCLUSIVE,LEFT_RIGHT_NOWRAP,motion.count); - XVimRange r = [self xvim_getMotionRange:self.insertionPoint Motion:m]; - if( r.end == NSNotFound){ - return; - } - if( m.info->reachedEndOfLine ){ - [self xvim_swapCaseForRange:[self xvim_getOperationRangeFrom:r.begin To:r.end Type:CHARACTERWISE_INCLUSIVE]]; - }else{ - [self xvim_swapCaseForRange:[self xvim_getOperationRangeFrom:r.begin To:r.end Type:CHARACTERWISE_EXCLUSIVE]]; - } - [self xvim_moveCursor:r.end preserveColumn:NO]; - }else{ - NSRange r; - XVimRange to = [self xvim_getMotionRange:self.insertionPoint Motion:motion]; - if( to.end == NSNotFound){ - return; - } - r = [self xvim_getOperationRangeFrom:to.begin To:to.end Type:motion.type]; - [self xvim_swapCaseForRange:r]; - [self xvim_moveCursor:r.location preserveColumn:NO]; - } - }else{ - NSArray* ranges = [self xvim_selectedRanges]; - for( NSValue* val in ranges){ - [self xvim_swapCaseForRange:[val rangeValue]]; - } - [self xvim_moveCursor:[[ranges objectAtIndex:0] rangeValue].location preserveColumn:NO]; - } - - [self xvim_syncState]; - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; - -} - -- (void)xvim_makeLowerCase:(XVimMotion*)motion{ - if( self.insertionPoint == 0 && [[self xvim_string] length] == 0 ){ - return ; - } - - NSString* s = [self xvim_string]; - if( self.selectionMode == XVIM_VISUAL_NONE ){ - NSRange r; - XVimRange to = [self xvim_getMotionRange:self.insertionPoint Motion:motion]; - if( to.end == NSNotFound ){ - return; - } - r = [self xvim_getOperationRangeFrom:to.begin To:to.end Type:motion.type]; - [self insertText:[[s substringWithRange:r] lowercaseString] replacementRange:r]; - [self xvim_moveCursor:r.location preserveColumn:NO]; - }else{ - NSArray* ranges = [self xvim_selectedRanges]; - for( NSValue* val in ranges){ - [self insertText:[[s substringWithRange:val.rangeValue] lowercaseString] replacementRange:val.rangeValue]; - } - [self xvim_moveCursor:[[ranges objectAtIndex:0] rangeValue].location preserveColumn:NO]; - } - - [self xvim_syncState]; - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; -} - -- (void)xvim_makeUpperCase:(XVimMotion*)motion{ - if( self.insertionPoint == 0 && [[self xvim_string] length] == 0 ){ - return ; - } - - NSString* s = [self xvim_string]; - if( self.selectionMode == XVIM_VISUAL_NONE ){ - NSRange r; - XVimRange to = [self xvim_getMotionRange:self.insertionPoint Motion:motion]; - if( to.end == NSNotFound ){ - return; - } - r = [self xvim_getOperationRangeFrom:to.begin To:to.end Type:motion.type]; // TODO: use to.begin instead of insertionPoint - [self insertText:[[s substringWithRange:r] uppercaseString] replacementRange:r]; - [self xvim_moveCursor:r.location preserveColumn:NO]; - }else{ - NSArray* ranges = [self xvim_selectedRanges]; - for( NSValue* val in ranges){ - [self insertText:[[s substringWithRange:val.rangeValue] uppercaseString] replacementRange:val.rangeValue]; - } - [self xvim_moveCursor:[[ranges objectAtIndex:0] rangeValue].location preserveColumn:NO]; - } - - [self xvim_syncState]; - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; - -} - -- (BOOL)xvim_replaceCharacters:(unichar)c count:(NSUInteger)count{ - NSUInteger end = [self.textStorage xvim_endOfLine:self.insertionPoint]; - // Note : endOfLine may return one less than self.insertionPoint if self.insertionPoint is on newline - if( NSNotFound == end ){ - return NO; - } - NSUInteger num = end - self.insertionPoint + 1; - if( num < count ){ - return NO; - } - - end = self.insertionPoint+count; - for( NSUInteger pos = self.insertionPoint; pos < end; pos++){ - [self insertText:[NSString stringWithFormat:@"%c",c] replacementRange:NSMakeRange(pos, 1)]; - } - return YES; -} - -- (void)xvim_joinAtLineNumber:(NSUInteger)line{ - BOOL needSpace = NO; - NSUInteger headOfLine = [self.textStorage xvim_indexOfLineNumber:line]; - if( headOfLine == NSNotFound){ - return; - } - - NSUInteger tail = [self.textStorage xvim_endOfLine:headOfLine]; - if( [self.textStorage isEOF:tail] ){ - // This is the last line and nothing to join - return; - } - - // Check if we need to insert space between lines. - NSUInteger lastOfLine = [self.textStorage xvim_lastOfLine:headOfLine]; - if( lastOfLine != NSNotFound ){ - // This is not blank line so we check if the last character is space or not . - if( ![self.textStorage isWhitespace:lastOfLine] ){ - needSpace = YES; - } - } - - // Search in next line for the position to join(skip white spaces in next line) - NSUInteger posToJoin = [self.textStorage nextLine:headOfLine column:0 count:1 option:MOTION_OPTION_NONE]; - - posToJoin = [self.textStorage xvim_nextNonblankInLineAtIndex:posToJoin allowEOL:YES]; - if (![self.textStorage isEOF:posToJoin] && [self.string characterAtIndex:posToJoin] == ')') { - needSpace = NO; - } - - // delete "tail" to "posToJoin" excluding the position of "posToJoin" and insert space if need. - if( needSpace ){ - [self insertText:@" " replacementRange:NSMakeRange(tail, posToJoin-tail)]; - }else{ - [self insertText:@"" replacementRange:NSMakeRange(tail, posToJoin-tail)]; - } - - // Move cursor - [self xvim_moveCursor:tail preserveColumn:NO]; -} - -- (void)xvim_join:(NSUInteger)count addSpace:(BOOL)addSpace{ - NSUInteger line; - - [self xvim_registerInsertionPointForUndo]; - - if (self.selectionMode == XVIM_VISUAL_NONE) { - line = self.insertionLine; - } else { - XVimRange lines = [self _xvim_selectedLines]; - - line = lines.begin; - count = MAX(1, lines.end - lines.begin); - } - - if (addSpace) { - for (NSUInteger i = 0; i < count; i++) { - [self xvim_joinAtLineNumber:line]; - } - } else { - NSTextStorage *ts = self.textStorage; - NSUInteger pos = [ts xvim_indexOfLineNumber:line]; - - for (NSUInteger i = 0; i < count; i++) { - NSUInteger tail = [ts xvim_endOfLine:pos]; - - if (tail != NSNotFound && ![ts isEOF:tail]) { - [self insertText:@"" replacementRange:NSMakeRange(tail, 1)]; - [self xvim_moveCursor:tail preserveColumn:NO]; - } - } - } - - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; -} - -- (void)xvim_filter:(XVimMotion*)motion{ - if (self.insertionPoint == 0 && [[self xvim_string] length] == 0) { - return ; - } - - NSUInteger insertionAfterFilter = self.insertionPoint; - NSRange filterRange; - if (self.selectionMode == XVIM_VISUAL_NONE) { - XVimRange to = [self xvim_getMotionRange:self.insertionPoint Motion:motion]; - if (to.end == NSNotFound) { - return; - } - filterRange = [self xvim_getOperationRangeFrom:to.begin To:to.end Type:LINEWISE]; - } else { - XVimRange lines = [self _xvim_selectedLines]; - NSUInteger from = [self.textStorage xvim_indexOfLineNumber:lines.begin]; - NSUInteger to = [self.textStorage xvim_indexOfLineNumber:lines.end]; - filterRange = [self xvim_getOperationRangeFrom:from To:to Type:LINEWISE]; - } - - [self xvim_indentCharacterRange:filterRange]; - [self xvim_moveCursor:insertionAfterFilter preserveColumn:NO]; - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; -} - -- (void)_xvim_shift:(XVimMotion*)motion right:(BOOL)right -{ - if (self.insertionPoint == 0 && [[self xvim_string] length] == 0) { - return ; - } - - NSTextStorage *ts = self.textStorage; - NSUInteger shiftWidth = ts.xvim_indentWidth; - NSUInteger column = 0; - XVimRange lines; - BOOL blockMode = NO; - NSUndoManager *undoManager = self.undoManager; - - if (self.selectionMode == XVIM_VISUAL_NONE) { - XVimRange to = [self xvim_getMotionRange:self.insertionPoint Motion:motion]; - if (to.end == NSNotFound) { - return; - } - lines = XVimMakeRange([ts xvim_lineNumberAtIndex:to.begin], [ts xvim_lineNumberAtIndex:to.end]); - } else if (self.selectionMode != XVIM_VISUAL_BLOCK) { - lines = [self _xvim_selectedLines]; - shiftWidth *= motion.count; - } else { - XVimSelection sel = [self _xvim_selectedBlock]; - - column = sel.left; - lines = XVimMakeRange(sel.top, sel.bottom); - blockMode = YES; - shiftWidth *= motion.count; - } - - NSUInteger pos = [ts xvim_indexOfLineNumber:lines.begin column:0]; - [undoManager setGroupsByEvent:NO]; - [undoManager beginUndoGrouping]; - - if (!blockMode) { - [self xvim_registerPositionForUndo:[ts xvim_firstNonblankInLineAtIndex:pos allowEOL:YES]]; - } - - if (right) { - NSString *s = [NSString stringMadeOfSpaces:shiftWidth]; - [self xvim_blockInsertFixupWithText:s mode:XVIM_INSERT_SPACES count:1 column:column lines:lines]; - } else { - for (NSUInteger line = lines.begin; line <= lines.end; line++) { - [self _xvim_removeSpacesAtLine:line column:column count:shiftWidth]; - } - } - - if (blockMode) { - pos = [ts xvim_indexOfLineNumber:lines.begin column:column]; - } else { - pos = [ts xvim_firstNonblankInLineAtIndex:pos allowEOL:YES]; - } - - [self xvim_moveCursor:pos preserveColumn:NO]; - [undoManager endUndoGrouping]; - [undoManager setGroupsByEvent:YES]; - - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; -} - -- (void)xvim_shiftRight:(XVimMotion*)motion{ - [self _xvim_shift:motion right:YES]; -} - -- (void)xvim_shiftLeft:(XVimMotion*)motion{ - [self _xvim_shift:motion right:NO]; -} - -- (void)xvim_insertText:(NSString*)str line:(NSUInteger)line column:(NSUInteger)column{ - NSUInteger pos = [self.textStorage xvim_indexOfLineNumber:line column:column]; - if( pos == NSNotFound ){ - return; - } - [self insertText:str replacementRange:NSMakeRange(pos,0)]; -} - -- (void)xvim_insertNewlineBelowLine:(NSUInteger)line{ - NSAssert( line != 0, @"line number starts from 1"); - NSUInteger pos = [self.textStorage xvim_indexOfLineNumber:line]; - if( NSNotFound == pos ){ - return; - } - pos = [self.textStorage xvim_endOfLine:pos]; - [self insertText:@"\n" replacementRange:NSMakeRange(pos ,0)]; - [self xvim_moveCursor:pos+1 preserveColumn:NO]; - [self xvim_syncState]; -} - -- (void)xvim_insertNewlineBelowCurrentLine{ - [self xvim_insertNewlineBelowLine:[self.textStorage xvim_lineNumberAtIndex:self.insertionPoint]]; -} - -- (void)xvim_insertNewlineBelowCurrentLineWithIndent{ - NSUInteger tail = [self.textStorage xvim_endOfLine:self.insertionPoint]; - [self setSelectedRange:NSMakeRange(tail,0)]; - [self insertNewline:self]; -} - -- (void)xvim_insertNewlineAboveLine:(NSUInteger)line{ - NSAssert( line != 0, @"line number starts from 1"); - NSUInteger pos = [self.textStorage xvim_indexOfLineNumber:line]; - if( NSNotFound == pos ){ - return; - } - if( 1 != line ){ - [self xvim_insertNewlineBelowLine:line-1]; - }else{ - [self insertText:@"\n" replacementRange:NSMakeRange(0,0)]; - [self setSelectedRange:NSMakeRange(0,0)]; - } -} - -- (void)xvim_insertNewlineAboveCurrentLine{ - [self xvim_insertNewlineAboveLine:[self.textStorage xvim_lineNumberAtIndex:self.insertionPoint]]; -} - -- (void)xvim_insertNewlineAboveCurrentLineWithIndent{ - NSUInteger head = [self.textStorage xvim_firstOfLine:self.insertionPoint]; - if( NSNotFound == head ){ - head = self.insertionPoint; - } - if( 0 != head ){ - [self setSelectedRange:NSMakeRange(head-1,0)]; - [self insertNewline:self]; - }else{ - [self setSelectedRange:NSMakeRange(head,0)]; - [self insertNewline:self]; - [self setSelectedRange:NSMakeRange(0,0)]; - } -} - -- (void)xvim_insertNewlineAboveAndInsertWithIndent{ - self.cursorMode = CURSOR_MODE_INSERT; - [self xvim_insertNewlineAboveCurrentLineWithIndent]; -} - -- (void)xvim_insertNewlineBelowAndInsertWithIndent{ - self.cursorMode = CURSOR_MODE_INSERT; - [self xvim_insertNewlineBelowCurrentLineWithIndent]; -} - -- (void)xvim_insert:(XVimInsertionPoint)mode blockColumn:(NSUInteger *)column blockLines:(XVimRange *)lines{ - NSTextStorage *ts = self.textStorage; - - if (column) *column = NSNotFound; - if (lines) *lines = XVimMakeRange(NSNotFound, NSNotFound); - - if (self.selectionMode == XVIM_VISUAL_BLOCK) { - XVimSelection sel = [self _xvim_selectedBlock]; - - if (lines) *lines = XVimMakeRange(sel.top, sel.bottom); - switch (mode) { - case XVIM_INSERT_BLOCK_KILL_EOL: - sel.right = XVimSelectionEOL; - /* fallthrough */ - case XVIM_INSERT_BLOCK_KILL: - [self _xvim_yankSelection:sel]; - [self _xvim_killSelection:sel]; - /* falltrhough */ - case XVIM_INSERT_DEFAULT: - self.insertionPoint = [ts xvim_indexOfLineNumber:sel.top column:sel.left]; - if (column) *column = sel.left; - break; - case XVIM_INSERT_APPEND: - if (sel.right != XVimSelectionEOL) { - sel.right++; - } - self.insertionPoint = [ts xvim_indexOfLineNumber:sel.top column:sel.right]; - if (column) *column = sel.right; - break; - default: - NSAssert(false, @"unreachable"); - break; - } - } else if (mode != XVIM_INSERT_DEFAULT) { - NSUInteger pos = self.insertionPoint; - switch (mode) { - case XVIM_INSERT_APPEND_EOL: - self.insertionPoint = [ts xvim_endOfLine:pos]; - break; - case XVIM_INSERT_APPEND: - NSAssert(self.cursorMode == CURSOR_MODE_COMMAND, @"self.cursorMode shoud be CURSOR_MODE_COMMAND"); - if (![ts isEOF:pos] && ![ts isNewline:pos]){ - self.insertionPoint = pos + 1; - } - break; - case XVIM_INSERT_BEFORE_FIRST_NONBLANK: - self.insertionPoint = [ts xvim_firstNonblankInLineAtIndex:pos allowEOL:YES]; - break; - default: - NSAssert(false, @"unreachable"); - } - } - self.cursorMode = CURSOR_MODE_INSERT; - [self xvim_changeSelectionMode:XVIM_VISUAL_NONE]; - [self xvim_syncState]; -} - -- (void)xvim_overwriteCharacter:(unichar)c{ - if (self.insertionPoint >= self.textStorage.length) { - // Should not happen. - return; - } - [self insertText:[NSString stringWithFormat:@"%c",c] replacementRange:NSMakeRange(self.insertionPoint,1)]; - return; -} - -- (BOOL)xvim_incrementNumber:(int64_t)offset{ - NSUInteger ip = self.insertionPoint; - NSRange range; - - range = [self xvim_currentNumber]; - if (range.location == NSNotFound) { - NSUInteger pos = [self.textStorage xvim_nextDigitInLine:ip]; - if (pos == NSNotFound) { - return NO; - } - self.insertionPoint = pos; - range = [self xvim_currentNumber]; - if (range.location == NSNotFound) { - // should not happen - self.insertionPoint = ip; - return NO; - } - } - - [self xvim_registerPositionForUndo:ip]; - - const char *s = [[self.xvim_string substringWithRange:range] UTF8String]; - NSString *repl; - uint64_t u = strtoull(s, NULL, 0); - int64_t i = strtoll(s, NULL, 0); - - if (strncmp(s, "0x", 2) == 0) { - repl = [NSString stringWithFormat:@"0x%0*llx", (int)strlen(s) - 2, u + (uint64_t)offset]; - } else if (u && *s == '0' && s[1] && !strchr(s, '9') && !strchr(s, '8')) { - repl = [NSString stringWithFormat:@"0%0*llo", (int)strlen(s) - 1, u + (uint64_t)offset]; - } else if (u && *s == '+') { - repl = [NSString stringWithFormat:@"%+lld", i + offset]; - } else { - repl = [NSString stringWithFormat:@"%lld", i + offset]; - } - - [self insertText:repl replacementRange:range]; - [self xvim_moveCursor:range.location + repl.length - 1 preserveColumn:NO]; - return YES; -} - -- (void)xvim_blockInsertFixupWithText:(NSString *)text mode:(XVimInsertionPoint)mode - count:(NSUInteger)count column:(NSUInteger)column lines:(XVimRange)lines -{ - NSMutableString *buf = nil; - NSTextStorage *ts; - NSUInteger tabWidth; - - if (count == 0 || lines.begin > lines.end || text.length == 0) { - return; - } - if ([text rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location != NSNotFound) { - return; - } - if (count > 1) { - buf = [[NSMutableString alloc] initWithCapacity:text.length * count]; - for (NSUInteger i = 0; i < count; i++) { - [buf appendString:text]; - } - text = buf; - } - - ts = self.textStorage; - tabWidth = ts.xvim_tabWidth; - - for (NSUInteger line = lines.begin; line <= lines.end; line++) { - NSUInteger pos = [ts xvim_indexOfLineNumber:line column:column]; - - if (column != XVimSelectionEOL && [ts isEOL:pos]) { - if (mode == XVIM_INSERT_SPACES && column == 0) { - continue; - } - if ([ts xvim_columnOfIndex:pos] < column) { - if (mode != XVIM_INSERT_APPEND) { - continue; - } - [self _xvim_insertSpaces:column - [ts xvim_columnOfIndex:pos] replacementRange:NSMakeRange(pos, 0)]; - } - } - if (tabWidth && [self.xvim_string characterAtIndex:pos] == '\t') { - NSUInteger col = [ts xvim_columnOfIndex:pos]; - - if (col < column) { - [self _xvim_insertSpaces:tabWidth - (col % tabWidth) replacementRange:NSMakeRange(pos, 1)]; - pos += column - col; - } - } - [self insertText:text replacementRange:NSMakeRange(pos, 0)]; - } - - [buf release]; -} - -- (void)xvim_sortLinesFrom:(NSUInteger)line1 to:(NSUInteger)line2 withOptions:(XVimSortOptions)options{ - NSAssert( line1 > 0, @"line1 must be greater than 0."); - NSAssert( line2 > 0, @"line2 must be greater than 0."); - - if( line2 < line1 ){ - //swap - NSUInteger tmp = line1; - line1 = line2; - line2 = tmp; - } - - NSRange characterRange = [self.textStorage xvim_indexRangeForLines:NSMakeRange(line1, line2 - line1 + 1)]; - NSString *str = [[self xvim_string] substringWithRange:characterRange]; - - NSMutableArray *lines = [[[str componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] mutableCopy] autorelease]; - if ([[lines lastObject] length] == 0) { - [lines removeLastObject]; - } - [lines sortUsingComparator:^NSComparisonResult(NSString *str1, NSString *str2) { - NSStringCompareOptions compareOptions = 0; - if (options & XVimSortOptionNumericSort) { - compareOptions |= NSNumericSearch; - } - if (options & XVimSortOptionIgnoreCase) { - compareOptions |= NSCaseInsensitiveSearch; - } - - if (options & XVimSortOptionReversed) { - return [str2 compare:str1 options:compareOptions]; - } else { - return [str1 compare:str2 options:compareOptions]; - } - }]; - - if (options & XVimSortOptionRemoveDuplicateLines) { - NSMutableIndexSet *removeIndices = [NSMutableIndexSet indexSet]; - // At this point the lines are already sorted - [lines enumerateObjectsUsingBlock:^(NSString *str, NSUInteger idx, BOOL *stop) { - if (idx < [lines count] - 1) { - NSString *nextStr = [lines objectAtIndex:idx + 1]; - if ([str isEqualToString:nextStr]) { - [removeIndices addIndex:idx + 1]; - } - } - }]; - [lines removeObjectsAtIndexes:removeIndices]; - } - - NSUInteger insertionAfterOperation = characterRange.location; - NSString *sortedLinesString = [[lines componentsJoinedByString:@"\n"] stringByAppendingString:@"\n"]; - if( [self shouldChangeTextInRange:characterRange replacementString:sortedLinesString] ){ - [self replaceCharactersInRange:characterRange withString:sortedLinesString]; - [self didChangeText]; - } - self.insertionPoint = insertionAfterOperation; - [self xvim_syncState]; -} - -- (void)xvim_selectNextPlaceholder { -#ifdef __USE_DVTKIT__ - if( [self isKindOfClass:[DVTSourceTextView class]] ){ - [(DVTSourceTextView*)self selectNextPlaceholder:self]; - } -#endif -} - -- (void)xvim_selectPreviousPlaceholder { -#ifdef __USE_DVTKIT__ - if( [self isKindOfClass:[DVTSourceTextView class]] ){ - [(DVTSourceTextView*)self selectPreviousPlaceholder:self]; - } -#endif -} - -- (void)xvim_hideCompletions { -#ifdef __USE_DVTKIT__ - if( [self isKindOfClass:[DVTSourceTextView class]] ){ - [((DVTSourceTextView*)self).completionController hideCompletions]; - } -#endif -} - -#pragma mark Scroll -- (NSUInteger)xvim_lineUp:(NSUInteger)index count:(NSUInteger)count { - [self scrollLineUp:self]; - NSRect visibleRect = [[self enclosingScrollView] contentView].bounds; - NSRect currentInsertionRect = [[self layoutManager] boundingRectForGlyphRange:NSMakeRange(index,0) inTextContainer:[self textContainer]]; - NSPoint relativeInsertionPoint = SubPoint(currentInsertionRect.origin, visibleRect.origin); - if (relativeInsertionPoint.y > visibleRect.size.height) { - [self moveUp:self]; - NSPoint newPoint = [[self layoutManager] boundingRectForGlyphRange:[self selectedRange] inTextContainer:[self textContainer]].origin; - index = [self xvim_glyphIndexForPoint:newPoint]; - } - return index; -} - -- (NSUInteger)xvim_lineDown:(NSUInteger)index count:(NSUInteger)count { - [self scrollLineDown:self]; - NSRect visibleRect = [[self enclosingScrollView] contentView].bounds; - NSRect currentInsertionRect = [[self layoutManager] boundingRectForGlyphRange:NSMakeRange(index,0) inTextContainer:[self textContainer]]; - if (currentInsertionRect.origin.y < visibleRect.origin.y) { - [self moveDown:self]; - NSPoint newPoint = NSMakePoint(currentInsertionRect.origin.x, visibleRect.origin.y); - index = [self xvim_glyphIndexForPoint:newPoint]; - } - return index; -} - -- (void)xvim_scroll:(CGFloat)ratio count:(NSUInteger)count{ - NSScrollView *scrollView = [self enclosingScrollView]; - NSRect visibleRect = [scrollView contentView].bounds; - CGFloat scrollSize = visibleRect.size.height * ratio * count; - NSPoint scrollPoint = NSMakePoint(visibleRect.origin.x, visibleRect.origin.y + scrollSize ); // This may be beyond the beginning or end of document (intentionally) - - // Cursor position relative to left-top origin shold be kept after scroll ( Exception is when it scrolls beyond the beginning or end of document) - NSRect currentInsertionRect = [self xvim_boundingRectForGlyphIndex:self.insertionPoint]; - NSPoint relativeInsertionPoint = SubPoint(currentInsertionRect.origin, visibleRect.origin); - //TRACE_LOG(@"Rect:%f %f realIndex:%d foldedIndex:%d", currentInsertionRect.origin.x, currentInsertionRect.origin.y, self.insertionPoint, index); - - // Cursor Position after scroll - NSPoint cursorAfterScroll = AddPoint(scrollPoint,relativeInsertionPoint); - - // Nearest character index to the cursor position after scroll - // TODO: consider blank-EOF line. Xcode does not return blank-EOF index with following method... - NSUInteger cursorIndexAfterScroll= [self xvim_glyphIndexForPoint:cursorAfterScroll]; - - // We do not want to change the insert point relative position from top of visible rect - // We have to calc the distance between insertion point befor/after scrolling to keep the position. - NSRect insertionRectAfterScroll = [self xvim_boundingRectForGlyphIndex:cursorIndexAfterScroll]; - NSPoint relativeInsertionPointAfterScroll = SubPoint(insertionRectAfterScroll.origin, scrollPoint); - CGFloat heightDiff = relativeInsertionPointAfterScroll.y - relativeInsertionPoint.y; - scrollPoint.y += heightDiff; - // Prohibit scroll beyond the bounds of document - if( scrollPoint.y > [[scrollView documentView] frame].size.height - visibleRect.size.height ){ - scrollPoint.y = [[scrollView documentView] frame].size.height - visibleRect.size.height ; - } else if (scrollPoint.y < 0.0) { - scrollPoint.y = 0.0; - } - - [[scrollView contentView] scrollToPoint:scrollPoint]; - [scrollView reflectScrolledClipView:[scrollView contentView]]; - - cursorIndexAfterScroll = [self.textStorage xvim_firstNonblankInLineAtIndex:cursorIndexAfterScroll allowEOL:YES]; - [self xvim_moveCursor:cursorIndexAfterScroll preserveColumn:NO]; - [self xvim_syncState]; - -} - -- (void)xvim_scrollBottom:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb{ // zb / z- - [self xvim_scrollCommon_moveCursorPos:lineNumber firstNonblank:fnb]; - NSScrollView *scrollView = [self enclosingScrollView]; - NSTextContainer *container = [self textContainer]; - NSRect glyphRect = [[self layoutManager] boundingRectForGlyphRange:NSMakeRange(self.insertionPoint,0) inTextContainer:container]; - NSPoint bottom = NSMakePoint(0.0f, NSMidY(glyphRect) + NSHeight(glyphRect) / 2.0f); - bottom.y -= NSHeight([[scrollView contentView] bounds]); - if( bottom.y < 0.0 ){ - bottom.y = 0.0; - } - [[scrollView contentView] scrollToPoint:bottom]; - [scrollView reflectScrolledClipView:[scrollView contentView]]; -} - -- (void)xvim_scrollCenter:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb{ // zz / z. - [self xvim_scrollCommon_moveCursorPos:lineNumber firstNonblank:fnb]; - NSScrollView *scrollView = [self enclosingScrollView]; - NSTextContainer *container = [self textContainer]; - NSRect glyphRect = [[self layoutManager] boundingRectForGlyphRange:NSMakeRange(self.insertionPoint,0) inTextContainer:container]; - NSPoint center = NSMakePoint(0.0f, NSMidY(glyphRect) - NSHeight(glyphRect) / 2.0f); - center.y -= NSHeight([[scrollView contentView] bounds]) / 2.0f; - if( center.y < 0.0 ){ - center.y = 0.0; - } - [[scrollView contentView] scrollToPoint:center]; - [scrollView reflectScrolledClipView:[scrollView contentView]]; -} - -- (void)xvim_scrollTop:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb{ // zt / z - [self xvim_scrollCommon_moveCursorPos:lineNumber firstNonblank:fnb]; - NSScrollView *scrollView = [self enclosingScrollView]; - NSTextContainer *container = [self textContainer]; - NSRect glyphRect = [[self layoutManager] boundingRectForGlyphRange:NSMakeRange(self.insertionPoint,0) inTextContainer:container]; - NSPoint top = NSMakePoint(0.0f, NSMidY(glyphRect) - NSHeight(glyphRect) / 2.0f); - [[scrollView contentView] scrollToPoint:top]; - [scrollView reflectScrolledClipView:[scrollView contentView]]; -} - -- (void)xvim_scrollTo:(NSUInteger)location { - // Update: I do not know if we really need Following block. - // It looks that they need it to call ensureLayoutForGlyphRange but do not know when it needed - // What I changed was the way calc "glyphRec". Not its using [self boundingRectForGlyphIndex] which coniders - // text folding when calc the rect. - /* - BOOL isBlankline = - (location == [[self string] length] || isNewline([[self string] characterAtIndex:location])) && - (location == 0 || isNewline([[self string] characterAtIndex:location-1])); - - NSRange characterRange; - characterRange.location = location; - characterRange.length = isBlankline ? 0 : 1; - - // Must call ensureLayoutForGlyphRange: to fix a bug where it will not scroll - // to the appropriate glyph due to non contiguous layout - NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:characterRange actualCharacterRange:NULL]; - [[self layoutManager] ensureLayoutForGlyphRange:NSMakeRange(0, glyphRange.location + glyphRange.length)]; - */ - - NSScrollView *scrollView = [self enclosingScrollView]; - NSRect glyphRect = [self xvim_boundingRectForGlyphIndex:location]; - - CGFloat glyphLeft = NSMidX(glyphRect) - NSWidth(glyphRect) / 2.0f; - CGFloat glyphRight = NSMidX(glyphRect) + NSWidth(glyphRect) / 2.0f; - - NSRect contentRect = [[scrollView contentView] bounds]; - CGFloat viewLeft = contentRect.origin.x; - CGFloat viewRight = contentRect.origin.x + NSWidth(contentRect); - - NSPoint scrollPoint = contentRect.origin; - if (glyphRight > viewRight){ - scrollPoint.x = glyphLeft - NSWidth(contentRect) / 2.0f; - }else if (glyphLeft < viewLeft){ - scrollPoint.x = glyphRight - NSWidth(contentRect) / 2.0f; - } - - CGFloat glyphBottom = NSMidY(glyphRect) + NSHeight(glyphRect) / 2.0f; - CGFloat glyphTop = NSMidY(glyphRect) - NSHeight(glyphRect) / 2.0f; - - CGFloat viewTop = contentRect.origin.y; - CGFloat viewBottom = contentRect.origin.y + NSHeight(contentRect); - - if (glyphTop < viewTop){ - if (viewTop - glyphTop > NSHeight(contentRect)){ - scrollPoint.y = glyphBottom - NSHeight(contentRect) / 2.0f; - }else{ - scrollPoint.y = glyphTop; - } - }else if (glyphBottom > viewBottom){ - if (glyphBottom - viewBottom > NSHeight(contentRect)) { - scrollPoint.y = glyphBottom - NSHeight(contentRect) / 2.0f; - }else{ - scrollPoint.y = glyphBottom - NSHeight(contentRect); - } - } - - scrollPoint.x = MAX(0, scrollPoint.x); - scrollPoint.y = MAX(0, scrollPoint.y); - - [[scrollView contentView] scrollToPoint:scrollPoint]; - [scrollView reflectScrolledClipView:[scrollView contentView]]; -} - -- (void)xvim_pageForward:(NSUInteger)index count:(NSUInteger)count { // C-f - [self xvim_scroll:1.0 count:count]; -} - -- (void)xvim_pageBackward:(NSUInteger)index count:(NSUInteger)count { // C-b - [self xvim_scroll:-1.0 count:count]; -} - -- (void)xvim_halfPageForward:(NSUInteger)index count:(NSUInteger)count { // C-d - [self xvim_scroll:0.5 count:count]; -} - -- (void)xvim_halfPageBackward:(NSUInteger)index count:(NSUInteger)count { // C-u - [self xvim_scroll:-0.5 count:count]; -} - -- (void)xvim_scrollPageForward:(NSUInteger)count{ - [self xvim_pageForward:self.insertionPoint count:count]; -} - -- (void)xvim_scrollPageBackward:(NSUInteger)count{ - [self xvim_pageBackward:self.insertionPoint count:count]; -} - -- (void)xvim_scrollHalfPageForward:(NSUInteger)count{ - [self xvim_halfPageForward:self.insertionPoint count:count]; -} - -- (void)xvim_scrollHalfPageBackward:(NSUInteger)count{ - [self xvim_halfPageBackward:self.insertionPoint count:count]; -} - -- (void)xvim_scrollLineForward:(NSUInteger)count{ - [self xvim_lineDown:self.insertionPoint count:count]; -} - -- (void)xvim_scrollLineBackward:(NSUInteger)count{ - [self xvim_lineUp:self.insertionPoint count:count]; -} - -#pragma mark Search -// Thanks to http://lists.apple.com/archives/cocoa-dev/2005/Jun/msg01909.html -- (NSRange)xvim_visibleRange:(NSTextView *)tv{ - NSScrollView *sv = [tv enclosingScrollView]; - if(!sv) return NSMakeRange(0,0); - NSLayoutManager *lm = [tv layoutManager]; - NSRect visRect = [tv visibleRect]; - - NSPoint tco = [tv textContainerOrigin]; - visRect.origin.x -= tco.x; - visRect.origin.y -= tco.y; - - NSRange glyphRange = [lm glyphRangeForBoundingRect:visRect inTextContainer:[tv textContainer]]; - NSRange charRange = [lm characterRangeForGlyphRange:glyphRange actualGlyphRange:nil]; - return charRange; -} - -- (void)xvim_highlightNextSearchCandidate:(NSString *)regex count:(NSUInteger)count option:(MOTION_OPTION)opt forward:(BOOL)forward{ - NSRange range = NSMakeRange(NSNotFound,0); - if( forward ){ - range = [self.textStorage searchRegexForward:regex from:self.insertionPoint count:count option:opt]; - }else{ - range = [self.textStorage searchRegexBackward:regex from:self.insertionPoint count:count option:opt]; - } - if( range.location != NSNotFound ){ - [self scrollRectToVisible:[self xvim_boundingRectForGlyphIndex:range.location]]; - [self showFindIndicatorForRange:range]; - } -} - -- (void)xvim_highlightNextSearchCandidateForward:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt{ - [self xvim_highlightNextSearchCandidate:regex count:count option:opt forward:YES]; -} - -- (void)xvim_highlightNextSearchCandidateBackward:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt{ - [self xvim_highlightNextSearchCandidate:regex count:count option:opt forward:NO]; -} - -- (void)xvim_updateFoundRanges:(NSString*)pattern withOption:(MOTION_OPTION)opt{ - NSAssert( nil != pattern, @"pattern munst not be nil"); - if( !self.needsUpdateFoundRanges ){ - return; - } - - NSRegularExpressionOptions r_opts = NSRegularExpressionAnchorsMatchLines; - if ( opt & SEARCH_CASEINSENSITIVE ){ - r_opts |= NSRegularExpressionCaseInsensitive; - } - - NSError *error = nil; - NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:r_opts error:&error]; - if( nil != error){ - [self.foundRanges removeAllObjects]; - return; - } - - // Find all the maches - NSString* string = self.string; - //NSTextStorage* storage = self.textStorage; - if( string == nil ){ - return; - } - NSArray* matches = [regex matchesInString:string options:0 range:NSMakeRange(0, string.length)]; - [self.foundRanges setArray:matches]; - - // Clear current highlight. - [self xvim_clearHighlightText]; - // Add yellow highlight - for( NSTextCheckingResult* result in self.foundRanges){ - [self.layoutManager addTemporaryAttribute:NSBackgroundColorAttributeName value:[NSColor yellowColor] forCharacterRange:result.range]; - } - - [self setNeedsUpdateFoundRanges:NO]; -} - -- (void)xvim_clearHighlightText{ - if( !self.needsUpdateFoundRanges ){ - return; - } - NSString* string = self.string; - [self.layoutManager removeTemporaryAttribute:NSBackgroundColorAttributeName forCharacterRange:NSMakeRange(0,string.length)]; - // [self.layoutManager addTemporaryAttribute:NSBackgroundColorAttributeName value:[NSColor clearColor] forCharacterRange:NSMakeRange(0, string.length)]; - [self setNeedsUpdateFoundRanges:NO]; -} - -- (NSRange)xvim_currentWord:(MOTION_OPTION)opt{ - return [self.textStorage currentWord:self.insertionPoint count:1 option:opt|TEXTOBJECT_INNER]; -} - -- (NSRange)xvim_currentNumber{ - NSUInteger insertPoint = self.insertionPoint; - NSUInteger n_start, n_end; - NSUInteger x_start, x_end; - NSString *s = self.xvim_string; - unichar c; - BOOL isOctal = YES; - - n_start = insertPoint; - while (n_start > 0 && [s isDigit:n_start - 1]) { - if (![s isOctDigit:n_start]) { - isOctal = NO; - } - n_start--; - } - n_end = insertPoint; - while (n_end < s.length && [s isDigit:n_end]) { - if (![s isOctDigit:n_end]) { - isOctal = NO; - } - n_end++; - } - - x_start = n_start; - while (x_start > 0 && [s isHexDigit:x_start - 1]) { - x_start--; - } - x_end = n_end; - while (x_end < s.length && [s isHexDigit:x_end]) { - x_end++; - } - - // first deal with Hex: 0xNNNNN - // case 1: check for insertion point on the '0' or 'x' - if (x_end - x_start == 1) { - NSUInteger end = x_end; - if (end < s.length && [s characterAtIndex:end] == 'x') { - do { - end++; - } while (end < s.length && [s isHexDigit:end]); - if (insertPoint < end && end - x_start > 2) { - // YAY it's hex for real!!! - return NSMakeRange(x_start, end - x_start); - } - } - } - - // case 2: check whether we're after 0x - if (insertPoint < x_end && x_end - x_start >= 1) { - if (x_start >= 2 && [s characterAtIndex:x_start - 1] == 'x' && [s characterAtIndex:x_start - 2] == '0') { - return NSMakeRange(x_start - 2, x_end - x_start + 2); - } - } - - if (insertPoint == n_end || n_start - n_end == 0) { - return NSMakeRange(NSNotFound, 0); - } - - // okay it's not hex, if it's not octal, check for leading +/- - if (n_start > 0 && !(isOctal && [s characterAtIndex:n_start] == '0')) { - c = [s characterAtIndex:n_start - 1]; - if (c == '+' || c == '-') { - n_start--; - } - } - return NSMakeRange(n_start, n_end - n_start); -} - -#pragma mark Search Position -/** - * Takes point in view and returns its index. - * This method automatically convert the "folded index" to "real index" - * When some characters are folded( like placeholders) the pure index for a specifix point is - * less than real index in the string. - **/ -- (NSUInteger)xvim_glyphIndexForPoint:(NSPoint)point { - return [[self layoutManager] glyphIndexForPoint:point inTextContainer:[self textContainer]]; -} - -- (NSRect)xvim_boundingRectForGlyphIndex:(NSUInteger)glyphIndex { - NSRect glyphRect; - if( [self.textStorage isEOF:glyphIndex] ){ - // When the index is EOF the range to specify here can not be grater than 0. If it is greater than 0 it returns (0,0) as a glyph rect. - glyphRect = [[self layoutManager] boundingRectForGlyphRange:NSMakeRange(glyphIndex, 0) inTextContainer:[self textContainer]]; - }else{ - glyphRect = [[self layoutManager] boundingRectForGlyphRange:NSMakeRange(glyphIndex, 1) inTextContainer:[self textContainer]]; - } - return glyphRect; -} - -/** - *Find and return an NSArray* with the placeholders in a current line. - * the placeholders are returned as NSValue* objects that encode NSRange structs. - * Returns an empty NSArray if there are no placeholders on the line. - */ --(NSArray*)xvim_placeholdersInLine:(NSUInteger)position{ - NSMutableArray* placeholders = [[NSMutableArray alloc] initWithCapacity:2]; - NSUInteger p = [self.textStorage xvim_firstOfLine:position]; - - for(NSUInteger curPos = p; curPos < [[self xvim_string] length]; curPos++){ - NSRange retval = [(DVTCompletingTextView*)self rangeOfPlaceholderFromCharacterIndex:curPos forward:YES wrap:NO limit:50]; - if(retval.location != NSNotFound){ - curPos = retval.location + retval.length; - [placeholders addObject:[NSValue valueWithRange:retval]]; - } - if ([self.textStorage isLOL:curPos] || [self.textStorage isEOF:curPos]) { - return [placeholders autorelease]; - } - } - - return [placeholders autorelease]; -} - - -#pragma mark helper methods - -- (void)xvim_syncStateFromView{ - // TODO: handle block selection (if selectedRanges have multiple ranges ) - if( self.xvim_lockSyncStateFromView ){ - return; - } - NSRange r = [self selectedRange]; - DEBUG_LOG(@"Selected Range(TotalLen:%d): Loc:%d Len:%d", self.string.length, r.location, r.length); - self.selectionMode = XVIM_VISUAL_NONE; - [self xvim_moveCursor:r.location preserveColumn:NO]; - self.selectionBegin = self.insertionPoint; -} - -@end - - -@implementation NSTextView(VimOperationPrivate) -#pragma mark Properties - -- (BOOL)xvim_lockSyncStateFromView{ - id ret = [self dataForName:@"lockSyncStateFromView"]; - return nil == ret ? NO : [ret boolValue]; -} - -- (void)setXvim_lockSyncStateFromView:(BOOL)lock{ - [self setBool:lock forName:@"lockSyncStateFromView"]; -} - -/** - * Returns start and end position of the specified motion. - * Note that this may return NSNotFound - **/ - -- (void)xvim_moveCursor:(NSUInteger)pos preserveColumn:(BOOL)preserve{ - // This method only update the internal state(like self.insertionPoint) - - if( pos > [self xvim_string].length){ - ERROR_LOG(@"Position specified exceeds the length of the text"); - pos = [self xvim_string].length; - } - - if( self.cursorMode == CURSOR_MODE_COMMAND && !(self.selectionMode == XVIM_VISUAL_BLOCK)){ - self.insertionPoint = [self.textStorage convertToValidCursorPositionForNormalMode:pos]; - }else{ - self.insertionPoint = pos; - } - - if( !preserve ){ - self.preservedColumn = [self.textStorage xvim_columnOfIndex:self.insertionPoint]; - } - - DEBUG_LOG(@"New Insertion Point:%d Preserved Column:%d", self.insertionPoint, self.preservedColumn); -} - -- (void)_adjustCursorPosition{ - if( ![self.textStorage isValidCursorPosition:self.insertionPoint] ){ - NSRange placeholder = [(DVTSourceTextView*)self rangeOfPlaceholderFromCharacterIndex:self.insertionPoint forward:NO wrap:NO limit:0]; - if( placeholder.location != NSNotFound && self.insertionPoint == (placeholder.location + placeholder.length)){ - //The condition here means that just before current insertion point is a placeholder. - //So we select the the place holder and its already selected by "selectedPreviousPlaceholder" above - [self xvim_moveCursor:placeholder.location preserveColumn:YES]; - }else{ - [self xvim_moveCursor:self.insertionPoint-1 preserveColumn:YES]; - } - } - -} - -/** - * Applies internal state to underlying view (self). - * This update self's property and applies the visual effect on it. - * All the state need to express Vim is held by this class and - * we use self to express it visually. - **/ -- (void)xvim_syncState{ - DEBUG_LOG(@"IP:%d", self.insertionPoint); - self.xvim_lockSyncStateFromView = YES; - // Reset current selection - if( self.cursorMode == CURSOR_MODE_COMMAND ){ - [self _adjustCursorPosition]; - } - [self dumpState]; - -#ifdef __XCODE5__ - [self setSelectedRanges:[self xvim_selectedRanges] affinity:NSSelectionAffinityDownstream stillSelecting:NO]; -#else - [(DVTFoldingTextStorage*)self.textStorage increaseUsingFoldedRanges]; - [self setSelectedRanges:[self xvim_selectedRanges] affinity:NSSelectionAffinityDownstream stillSelecting:NO]; - [(DVTFoldingTextStorage*)self.textStorage decreaseUsingFoldedRanges]; -#endif - [self xvim_scrollTo:self.insertionPoint]; - self.xvim_lockSyncStateFromView = NO; -} - -- (void)dumpState{ - LOG_STATE(); -} - -// xvim_setSelectedRange is an internal method -// This is used when you want to call [self setSelectedRrange]; -// The difference is that this checks the bounds(range can not be include EOF) and protect from Assersion -// Cursor can be on EOF but EOF can not be selected. -// It means that -// - setSelectedRange:NSMakeRange( indexOfEOF, 0 ) is allowed -// - setSelectedRange:NSMakeRange( indexOfEOF, 1 ) is not allowed -- (void)xvim_setSelectedRange:(NSRange)range{ - if( [self.textStorage isEOF:range.location] ){ - [self setSelectedRange:NSMakeRange(range.location,0)]; - return; - } - if( 0 == range.length ){ - // No need to check bounds - }else{ - NSUInteger lastIndex = range.location + range.length - 1; - if( [self.textStorage isEOF:lastIndex] ){ - range.length--; - }else{ - // No need to change the selection area - } - } - [self setSelectedRange:range]; - LOG_STATE(); -} - -- (NSArray*)xvim_selectedRanges{ - if (self.selectionMode != XVIM_VISUAL_BLOCK) { - return [NSArray arrayWithObject:[NSValue valueWithRange:[self _xvim_selectedRange]]]; - } - - NSMutableArray *rangeArray = [[[NSMutableArray alloc] init] autorelease]; - NSTextStorage *ts = self.textStorage; - XVimSelection sel = [self _xvim_selectedBlock]; - - for (NSUInteger line = sel.top; line <= sel.bottom; line++) { - NSUInteger begin = [ts xvim_indexOfLineNumber:line column:sel.left]; - NSUInteger end = [ts xvim_indexOfLineNumber:line column:sel.right]; - - if ([ts isEOF:begin]) { - continue; - } - if ([ts isEOF:end]){ - end--; - } else if (sel.right != XVimSelectionEOL && [ts isEOL:end]) { - end--; - } - [rangeArray addObject:[NSValue valueWithRange:NSMakeRange(begin, end - begin + 1)]]; - } - return rangeArray; -} - -- (XVimRange)xvim_getMotionRange:(NSUInteger)current Motion:(XVimMotion*)motion{ - NSRange range = NSMakeRange( NSNotFound , 0 ); - NSUInteger begin = current; - NSUInteger end = NSNotFound; - NSUInteger tmpPos = NSNotFound; - NSUInteger start = NSNotFound; - NSUInteger starts_end = NSNotFound; - - switch (motion.motion) { - case MOTION_NONE: - // Do nothing - break; - case MOTION_FORWARD: - end = [self.textStorage next:begin count:motion.count option:motion.option info:motion.info]; - break; - case MOTION_BACKWARD: - end = [self.textStorage prev:begin count:motion.count option:motion.option ]; - break; - case MOTION_WORD_FORWARD: - end = [self.textStorage wordsForward:begin count:motion.count option:motion.option info:motion.info]; - break; - case MOTION_WORD_BACKWARD: - end = [self.textStorage wordsBackward:begin count:motion.count option:motion.option]; - break; - case MOTION_END_OF_WORD_FORWARD: - end = [self.textStorage endOfWordsForward:begin count:motion.count option:motion.option]; - break; - case MOTION_END_OF_WORD_BACKWARD: - end = [self.textStorage endOfWordsBackward:begin count:motion.count option:motion.option]; - break; - case MOTION_LINE_FORWARD: - end = [self.textStorage nextLine:begin column:self.preservedColumn count:motion.count option:motion.option]; - break; - case MOTION_LINE_BACKWARD: - end = [self.textStorage prevLine:begin column:self.preservedColumn count:motion.count option:motion.option]; - break; - case MOTION_BEGINNING_OF_LINE: - end = [self.textStorage xvim_startOfLine:begin]; - if( end == NSNotFound){ - end = current; - } - break; - case MOTION_END_OF_LINE: - tmpPos = [self.textStorage nextLine:begin column:0 count:motion.count-1 option:MOTION_OPTION_NONE]; - end = [self.textStorage xvim_endOfLine:tmpPos]; - if( end == NSNotFound){ - end = tmpPos; - } - break; - case MOTION_SENTENCE_FORWARD: - end = [self.textStorage sentencesForward:begin count:motion.count option:motion.option]; - break; - case MOTION_SENTENCE_BACKWARD: - end = [self.textStorage sentencesBackward:begin count:motion.count option:motion.option]; - break; - case MOTION_PARAGRAPH_FORWARD: - end = [self.textStorage paragraphsForward:begin count:motion.count option:motion.option]; - break; - case MOTION_PARAGRAPH_BACKWARD: - end = [self.textStorage paragraphsBackward:begin count:motion.count option:motion.option]; - break; - case MOTION_NEXT_CHARACTER: - end = [self.textStorage nextCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; - break; - case MOTION_PREV_CHARACTER: - end = [self.textStorage prevCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; - break; - case MOTION_TILL_NEXT_CHARACTER: - end = [self.textStorage nextCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; - if(end != NSNotFound){ - end--; - } - break; - case MOTION_TILL_PREV_CHARACTER: - end = [self.textStorage prevCharacterInLine:begin count:motion.count character:motion.character option:MOTION_OPTION_NONE]; - if(end != NSNotFound){ - end++; - } - break; - case MOTION_NEXT_FIRST_NONBLANK: - end = [self.textStorage nextLine:begin column:0 count:motion.count option:motion.option]; - tmpPos = [self.textStorage xvim_nextNonblankInLineAtIndex:end allowEOL:NO]; - if( NSNotFound != tmpPos ){ - end = tmpPos; - } - break; - case MOTION_PREV_FIRST_NONBLANK: - end = [self.textStorage prevLine:begin column:0 count:motion.count option:motion.option]; - tmpPos = [self.textStorage xvim_nextNonblankInLineAtIndex:end allowEOL:NO]; - if( NSNotFound != tmpPos ){ - end = tmpPos; - } - break; - case MOTION_FIRST_NONBLANK: - end = [self.textStorage xvim_firstNonblankInLineAtIndex:begin allowEOL:NO]; - break; - case MOTION_LINENUMBER: - end = [self.textStorage xvim_indexOfLineNumber:motion.line column:self.preservedColumn]; - if( NSNotFound == end ){ - end = [self.textStorage xvim_indexOfLineNumber:[self.textStorage xvim_numberOfLines] column:self.preservedColumn]; - } - break; - case MOTION_PERCENT: - end = [self.textStorage xvim_indexOfLineNumber:1 + ([self.textStorage xvim_numberOfLines]-1) * motion.count/100]; - break; - case MOTION_NEXT_MATCHED_ITEM: - end = [self.textStorage positionOfMatchedPair:begin]; - break; - case MOTION_LASTLINE: - end = [self.textStorage xvim_indexOfLineNumber:[self.textStorage xvim_numberOfLines] column:self.preservedColumn]; - break; - case MOTION_HOME: - end = [self.textStorage xvim_firstNonblankInLineAtIndex:[self.textStorage xvim_indexOfLineNumber:[self xvim_lineNumberFromTop:motion.count]] allowEOL:YES]; - break; - case MOTION_MIDDLE: - end = [self.textStorage xvim_firstNonblankInLineAtIndex:[self.textStorage xvim_indexOfLineNumber:[self xvim_lineNumberAtMiddle]] allowEOL:YES]; - break; - case MOTION_BOTTOM: - end = [self.textStorage xvim_firstNonblankInLineAtIndex:[self.textStorage xvim_indexOfLineNumber:[self xvim_lineNumberFromBottom:motion.count]] allowEOL:YES]; - break; - case MOTION_SEARCH_FORWARD: - end = [self.textStorage searchRegexForward:motion.regex from:self.insertionPoint count:motion.count option:motion.option].location; - break; - case MOTION_SEARCH_BACKWARD: - end = [self.textStorage searchRegexBackward:motion.regex from:self.insertionPoint count:motion.count option:motion.option].location; - break; - case TEXTOBJECT_WORD: - range = [self.textStorage currentWord:begin count:motion.count option:motion.option]; - break; - case TEXTOBJECT_BRACES: - range = xv_current_block([self xvim_string], current, motion.count, !(motion.option & TEXTOBJECT_INNER), '{', '}'); - break; - case TEXTOBJECT_PARAGRAPH: - // Not supported - start = self.insertionPoint; - if(start != 0){ - start = [self.textStorage paragraphsBackward:self.insertionPoint count:1 option:MOPT_PARA_BOUND_BLANKLINE]; - } - starts_end = [self.textStorage paragraphsForward:start count:1 option:MOPT_PARA_BOUND_BLANKLINE]; - end = [self.textStorage paragraphsForward:self.insertionPoint count:motion.count option:MOPT_PARA_BOUND_BLANKLINE]; - - if(starts_end != end){ - start = starts_end; - } - range = NSMakeRange(start, end - start); - break; - case TEXTOBJECT_PARENTHESES: - range = xv_current_block([self xvim_string], current, motion.count, !(motion.option & TEXTOBJECT_INNER), '(', ')'); - break; - case TEXTOBJECT_SENTENCE: - // Not supported - break; - case TEXTOBJECT_ANGLEBRACKETS: - range = xv_current_block([self xvim_string], current, motion.count, !(motion.option & TEXTOBJECT_INNER), '<', '>'); - break; - case TEXTOBJECT_SQUOTE: - range = xv_current_quote([self xvim_string], current, motion.count, !(motion.option & TEXTOBJECT_INNER), '\''); - break; - case TEXTOBJECT_DQUOTE: - range = xv_current_quote([self xvim_string], current, motion.count, !(motion.option & TEXTOBJECT_INNER), '\"'); - break; - case TEXTOBJECT_TAG: - // Not supported - break; - case TEXTOBJECT_BACKQUOTE: - range = xv_current_quote([self xvim_string], current, motion.count, !(motion.option & TEXTOBJECT_INNER), '`'); - break; - case TEXTOBJECT_SQUAREBRACKETS: - range = xv_current_block([self xvim_string], current, motion.count, !(motion.option & TEXTOBJECT_INNER), '[', ']'); - break; - case MOTION_LINE_COLUMN: - end = [self.textStorage xvim_indexOfLineNumber:motion.line column:motion.column]; - if( NSNotFound == end ){ - end = current; - } - break; - case MOTION_POSITION: - end = motion.position; - break; - } - - if( range.location != NSNotFound ){// This block is for TEXTOBJECT - begin = range.location; - if( range.length == 0 ){ - end = NSNotFound; - }else{ - end = range.location + range.length - 1; - } - } - XVimRange r = XVimMakeRange(begin, end); - TRACE_LOG(@"range location:%u length:%u", r.begin, r.end); - return r; -} - -- (NSRange)xvim_getOperationRangeFrom:(NSUInteger)from To:(NSUInteger)to Type:(MOTION_TYPE)type { - if( [[self xvim_string] length] == 0 ){ - NSMakeRange(0,0); // No range - } - - if( from > to ){ - NSUInteger tmp = from; - from = to; - to = tmp; - } - // EOF can not be included in operation range. - if( [self.textStorage isEOF:from] ){ - return NSMakeRange(from, 0); // from is EOF but the length is 0 means EOF will not be included in the returned range. - } - - // EOF should not be included. - // If type is exclusive we do not subtract 1 because we do it later below - if( [self.textStorage isEOF:to] && type != CHARACTERWISE_EXCLUSIVE){ - to--; // Note that we already know that "to" is not 0 so not chekcing if its 0. - } - - // At this point "from" and "to" is not EOF - if( type == CHARACTERWISE_EXCLUSIVE ){ - // to will not be included. - to--; - }else if( type == CHARACTERWISE_INCLUSIVE ){ - // Nothing special - }else if( type == LINEWISE ){ - to = [self.textStorage xvim_endOfLine:to]; - if( [self.textStorage isEOF:to] ){ - to--; - } - NSUInteger head = [self.textStorage xvim_firstOfLine:from]; - if( NSNotFound != head ){ - from = head; - } - } - - return NSMakeRange(from, to - from + 1); // Inclusive range -} - -- (void)xvim_indentCharacterRange:(NSRange)range{ -#ifdef __USE_DVTKIT__ -#ifdef __XCODE5__ - if ( [self.textStorage isKindOfClass:[DVTTextStorage class]] ){ - [(DVTTextStorage*)self.textStorage indentCharacterRange:range undoManager:self.undoManager]; - } - return; -#else - if ( [self.textStorage isKindOfClass:[DVTSourceTextStorage class]] ){ - [(DVTSourceTextStorage*)self.textStorage indentCharacterRange:range undoManager:self.undoManager]; - } - return; -#endif -#else -#error You must implement here -#endif - - NSAssert(NO, @"You must implement here if you dont use this caregory with DVTSourceTextView"); -} - -#pragma mark scrolling -// This is used by scrollBottom,Top,Center as a common method -- (void)xvim_scrollCommon_moveCursorPos:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb{ - if( lineNumber != 0 ){ - NSUInteger pos = [self.textStorage xvim_indexOfLineNumber:lineNumber]; - if( NSNotFound == pos ){ - pos = self.textStorage.length; - } - [self xvim_moveCursor:pos preserveColumn:NO]; - [self xvim_syncState]; - } - if( fnb ){ - NSUInteger pos = [self.textStorage xvim_firstNonblankInLineAtIndex:self.insertionPoint allowEOL:YES]; - [self xvim_moveCursor:pos preserveColumn:NO]; - [self xvim_syncState]; - } -} - -- (NSUInteger)xvim_lineNumberFromBottom:(NSUInteger)count { // L - NSAssert( 0 != count , @"count starts from 1" ); - if( count > [self xvim_numberOfLinesInVisibleRect] ){ - count = [self xvim_numberOfLinesInVisibleRect]; - } - NSScrollView *scrollView = [self enclosingScrollView]; - NSTextContainer *container = [self textContainer]; - NSRect glyphRect = [[self layoutManager] boundingRectForGlyphRange:[self selectedRange] inTextContainer:container]; - NSPoint bottom = [[scrollView contentView] bounds].origin; - // This calculate the position of the bottom line and substruct height of "count" of lines to upwards - bottom.y += [[scrollView contentView] bounds].size.height - (NSHeight(glyphRect) / 2.0f) - (NSHeight(glyphRect) * (count-1)); - return [self.textStorage xvim_lineNumberAtIndex:[[scrollView documentView] characterIndexForInsertionAtPoint:bottom]]; -} - -- (NSUInteger)xvim_lineNumberAtMiddle{ - NSScrollView *scrollView = [self enclosingScrollView]; - NSPoint center = [[scrollView contentView] bounds].origin; - center.y += [[scrollView contentView] bounds].size.height / 2; - return [self.textStorage xvim_lineNumberAtIndex:[[scrollView documentView] characterIndexForInsertionAtPoint:center]]; -} - -- (NSUInteger)xvim_lineNumberFromTop:(NSUInteger)count{ - NSAssert( 0 != count , @"count starts from 1" ); - if( count > [self xvim_numberOfLinesInVisibleRect] ){ - count = [self xvim_numberOfLinesInVisibleRect]; - } - NSScrollView *scrollView = [self enclosingScrollView]; - NSTextContainer *container = [self textContainer]; - NSRect glyphRect = [[self layoutManager] boundingRectForGlyphRange:[self selectedRange] inTextContainer:container]; - NSPoint top = [[scrollView contentView] bounds].origin; - // Add height of "count" of lines to downwards - top.y += (NSHeight(glyphRect) / 2.0f) + (NSHeight(glyphRect) * (count-1)); - return [self.textStorage xvim_lineNumberAtIndex:[[scrollView documentView] characterIndexForInsertionAtPoint:top]]; -} - -- (NSRange)xvim_search:(NSString*)regex count:(NSUInteger)count option:(MOTION_OPTION)opt forward:(BOOL)forward{ - NSRange ret = NSMakeRange(NSNotFound, 0); - if( forward ){ - ret = [self.textStorage searchRegexForward:regex from:self.insertionPoint count:count option:opt]; - }else{ - ret = [self.textStorage searchRegexBackward:regex from:self.insertionPoint count:count option:opt]; - } - return ret; -} - -- (void)xvim_swapCaseForRange:(NSRange)range { - [self xvim_registerInsertionPointForUndo]; - NSString* text = [self xvim_string]; - - - NSMutableString *substring = [[text substringWithRange:range] mutableCopy]; - for (NSUInteger i = 0; i < range.length; ++i) { - NSRange currentRange = NSMakeRange(i, 1); - NSString *currentCase = [substring substringWithRange:currentRange]; - NSString *upperCase = [currentCase uppercaseString]; - - NSRange replaceRange = NSMakeRange(i, 1); - if ([currentCase isEqualToString:upperCase]){ - [substring replaceCharactersInRange:replaceRange withString:[currentCase lowercaseString]]; - }else{ - [substring replaceCharactersInRange:replaceRange withString:upperCase]; - } - } - - [self insertText:substring replacementRange:range]; - [substring release]; -} - -- (void)xvim_registerPositionForUndo:(NSUInteger)pos{ - [[self undoManager] registerUndoWithTarget:self selector:@selector(xvim_undoCursorPos:) object:[NSNumber numberWithUnsignedInteger:pos]]; -} - -- (void)xvim_registerInsertionPointForUndo{ - [self xvim_registerPositionForUndo:self.selectedRange.location]; -} - -- (void)xvim_undoCursorPos:(NSNumber*)num{ - [self xvim_moveCursor:[num unsignedIntegerValue] preserveColumn:NO]; - [self xvim_syncState]; -} -/* May be used later -- (void)hideCompletions { - [[[self xview] completionController] hideCompletions]; -} - -- (void)selectNextPlaceholder { - [[self xview] selectNextPlaceholder:self]; -} - -- (void)selectPreviousPlaceholder { - [[self xview] selectPreviousPlaceholder:self]; -} - */ -@end diff --git a/XVim/Test/XVimTestCase.m b/XVim/Test/XVimTestCase.m index 2138e2f7..19778315 100644 --- a/XVim/Test/XVimTestCase.m +++ b/XVim/Test/XVimTestCase.m @@ -13,9 +13,18 @@ #import "DVTSourceTextView+XVim.h" #import "XVimWindow.h" #import "XVimKeyStroke.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" @implementation XVimTestCase +@synthesize initialText; +@synthesize initialRange; +@synthesize input; +@synthesize expectedText; +@synthesize expectedRange; +@synthesize description; +@synthesize message; +@synthesize success; + + (XVimTestCase*)testCaseWithInitialText:(NSString*)it initialRange:(NSRange)ir input:(NSString*)in @@ -61,10 +70,15 @@ - (void)dealloc{ [super dealloc]; } -- (void)setUp{ - [[[XVimLastActiveSourceView() xvimWindow] sourceView] xvim_changeSelectionMode:XVIM_VISUAL_NONE]; - [XVimLastActiveSourceView() setString:self.initialText]; - [XVimLastActiveSourceView() setSelectedRange:self.initialRange]; +- (void)setUp +{ + XVimWindow *window = XVimLastActiveSourceView().xvimWindow; + XVimView *xview = window.currentView; + NSTextView *view = xview.textView; + + xview.selectionMode = XVIM_VISUAL_NONE; + [view setString:self.initialText]; + [view setSelectedRange:self.initialRange]; } - (BOOL)assert{ @@ -74,6 +88,7 @@ - (BOOL)assert{ } NSRange resultRange = [XVimLastActiveSourceView() selectedRange]; + if( self.expectedRange.location != resultRange.location || self.expectedRange.length != resultRange.length ){ diff --git a/XVim/XVim.h b/XVim/XVim.h index 0319ebe6..5b291bcb 100644 --- a/XVim/XVim.h +++ b/XVim/XVim.h @@ -9,7 +9,6 @@ #import #import "XVimDefs.h" #import "XVimKeymapProvider.h" -#import "XVimTextViewProtocol.h" #import "XVimKeyStroke.h" @class XVimKeymap; @@ -27,12 +26,15 @@ @class XVimTester; -extern NSString * const XVimDocumentChangedNotification; -extern NSString * const XVimDocumentPathKey; +extern NSString * const XVimBufferChangedNotification; +extern NSString * const XVimEnabledStatusChangedNotification; +extern NSString * const XVimBufferKey; @interface XVim : NSObject + (XVim*)instance; +@property (nonatomic, readonly) BOOL disabled; + @property (strong) XVimOptions* options; @property (strong) XVimSearch* searcher; @property (strong) XVimMotion* lastCharacterSearchMotion; @@ -43,10 +45,6 @@ extern NSString * const XVimDocumentPathKey; @property (readonly) XVimHistoryHandler* exCommandHistory; @property (readonly) XVimHistoryHandler* searchHistory; @property (readonly) XVimMutableString *lastOperationCommands; -@property XVIM_VISUAL_MODE lastVisualMode; -@property XVimPosition lastVisualPosition; -@property XVimPosition lastVisualSelectionBegin; -@property BOOL lastVisualSelectionToEOL; @property (nonatomic) BOOL isRepeating; // For dot(.) command repeat @property (copy) NSString* lastPlaybackRegister; diff --git a/XVim/XVim.m b/XVim/XVim.m index 8f878459..a08181ae 100644 --- a/XVim/XVim.m +++ b/XVim/XVim.m @@ -22,6 +22,7 @@ // #import "XVim.h" +#import "XVimBuffer.h" #import "Logger.h" #import "XVimSearch.h" #import "XVimExCommand.h" @@ -30,9 +31,7 @@ #import "XVimKeyStroke.h" #import "XVimOptions.h" #import "XVimHistoryHandler.h" -#import "XVimHookManager.h" #import "XVimCommandLine.h" -#import "DVTSourceTextViewHook.h" #import "XVimMarks.h" #import "XVimMotion.h" #import "XVimTester.h" @@ -40,8 +39,9 @@ #import "IDEKit.h" #import "objc/runtime.h" -NSString * const XVimDocumentChangedNotification = @"XVimDocumentChangedNotification"; -NSString * const XVimDocumentPathKey = @"XVimDocumentPathKey"; +NSString * const XVimBufferChangedNotification = @"XVimBufferChangedNotification"; +NSString * const XVimEnabledStatusChangedNotification = @"XVimBufferEnableNotification"; +NSString * const XVimBufferKey = @"XVimBufferKey"; @interface XVim() { XVimHistoryHandler *_exCommandHistory; @@ -56,6 +56,23 @@ - (void)parseRcFile; @end @implementation XVim +@synthesize disabled = _disabled; +@synthesize options = _options; +@synthesize searcher = _searcher; +@synthesize lastCharacterSearchMotion = _lastCharacterSearchMotion; +@synthesize excmd = _excmd; +@synthesize marks = _marks; +@synthesize testRunner = _testRunner; +@synthesize registerManager = _registerManager; +@synthesize exCommandHistory = _exCommandHistory; +@synthesize searchHistory = _searchHistory; +@synthesize lastOperationCommands = _lastOperationCommands; +@synthesize isRepeating = _isRepeating; +@synthesize tempRepeatRegister = _tempRepeatRegister; +@synthesize lastPlaybackRegister = _lastPlaybackRegister; +@synthesize document = _document; +@synthesize isExecuting = _isExecuting; + // For reverse engineering purpose. +(void)receiveNotification:(NSNotification*)notification{ @@ -116,7 +133,8 @@ + (void) addXVimMenu{ } -+ (void) load{ ++ (void) pluginDidLoad:(NSBundle *)plugin +{ NSBundle* app = [NSBundle mainBundle]; NSString* identifier = [app bundleIdentifier]; @@ -127,13 +145,9 @@ + (void) load{ // Entry Point of the Plugin. [Logger defaultLogger].level = LogTrace; - - // This looks strange but this is what intended to. - // [XVim instance] part initialize all the internal objects which does not depends on each other - // (If some initialization of a object which is held by XVim class(such as XVimSearch) access - // [XVim instance] inside it, it causes dead lock because of dispatch_once in [XVim instance] method. - // So after initializing all the independent object we do initialize dependent objects in init2 - [[XVim instance] init2]; + + // be sure XVim is initialized + (void)[XVim instance]; //Caution: parseRcFile can potentially invoke +instance on XVim (e.g. if "set ..." is //used in .ximvrc) so we must be sure to call it _AFTER_ +instance has completed @@ -148,7 +162,7 @@ + (void) load{ // Otherwise, we may miss some classes. // Command line window is not setuped if hook is too late. - [XVimHookManager hookWhenPluginLoaded]; + [XVimWindow class]; // We used to observer NSApplicationDidFinishLaunchingNotification to wait for all the classes in Xcode are loaded. // When notification comes we hook some classes so that we do not miss any classes. @@ -179,34 +193,29 @@ + (XVim*)instance{ - (id)init { if (self = [super init]) { self.options = [[[XVimOptions alloc] init] autorelease]; - } - return self; -} - -- (void)init2{ - _searchHistory = [[XVimHistoryHandler alloc] init]; - _searcher = [[XVimSearch alloc] init]; - _lastCharacterSearchMotion = nil; - _marks = [[XVimMarks alloc] init]; - _testRunner= [[XVimTester alloc] init]; - - self.excmd = [[[XVimExCommand alloc] init] autorelease]; - self.lastPlaybackRegister = nil; - self.registerManager = [[[XVimRegisterManager alloc] init] autorelease]; - self.lastOperationCommands = [[[XVimMutableString alloc] init] autorelease]; - self.lastVisualPosition = XVimMakePosition(NSNotFound, NSNotFound); - self.lastVisualSelectionBegin = XVimMakePosition(NSNotFound, NSNotFound); - self.tempRepeatRegister = [[[XVimMutableString alloc] init] autorelease]; - self.isRepeating = NO; - self.isExecuting = NO; - _logFile = nil; - _exCommandHistory = [[XVimHistoryHandler alloc] init]; - - for (int i = 0; i < XVIM_MODE_COUNT; ++i) { - _keymaps[i] = [[XVimKeymap alloc] init]; + _searchHistory = [[XVimHistoryHandler alloc] init]; + _searcher = [[XVimSearch alloc] init]; + _lastCharacterSearchMotion = nil; + _marks = [[XVimMarks alloc] init]; + _testRunner= [[XVimTester alloc] init]; + + self.excmd = [[[XVimExCommand alloc] init] autorelease]; + self.lastPlaybackRegister = nil; + self.registerManager = [[[XVimRegisterManager alloc] init] autorelease]; + self.lastOperationCommands = [[[XVimMutableString alloc] init] autorelease]; + self.tempRepeatRegister = [[[XVimMutableString alloc] init] autorelease]; + self.isRepeating = NO; + self.isExecuting = NO; + _logFile = nil; + _exCommandHistory = [[XVimHistoryHandler alloc] init]; + + for (int i = 0; i < XVIM_MODE_COUNT; ++i) { + _keymaps[i] = [[XVimKeymap alloc] init]; + } + + [_options addObserver:self forKeyPath:@"debug" options:NSKeyValueObservingOptionNew context:nil]; } - - [_options addObserver:self forKeyPath:@"debug" options:NSKeyValueObservingOptionNew context:nil]; + return self; } -(void)dealloc{ @@ -233,26 +242,34 @@ -(void)dealloc{ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ if( [keyPath isEqualToString:@"debug"]) { if( [[XVim instance] options].debug ){ - NSString *homeDir = NSHomeDirectoryForUser(NSUserName()); - NSString *logPath = [homeDir stringByAppendingString: @"/.xvimlog"]; + NSString *logPath = [@"~/.xvimlog" stringByExpandingTildeInPath]; [[Logger defaultLogger] setLogFile:logPath]; }else{ [[Logger defaultLogger] setLogFile:nil]; } } else if( [keyPath isEqualToString:@"document"] ){ - NSString *documentPath = [[[object document] fileURL] path]; - self.document = documentPath; - - if (documentPath != nil) { - NSDictionary *userInfo = [NSDictionary dictionaryWithObject:documentPath forKey:XVimDocumentPathKey]; - [[NSNotificationCenter defaultCenter] postNotificationName:XVimDocumentChangedNotification object:nil userInfo:userInfo]; + NSDocument *document = [object document]; + + if (![document respondsToSelector:@selector(textStorage)]) { + return; + } + + NSTextStorage *textStorage = [[object document] textStorage]; + XVimBuffer *buffer = document.xvim_buffer; + + if (!buffer && [document.fileURL isFileURL]) { + self.document = document.fileURL.path; + buffer = [XVimBuffer makeBufferForDocument:document textStorage:textStorage]; + } + if (buffer) { + NSDictionary *userInfo = @{ XVimBufferKey: buffer }; + [[NSNotificationCenter defaultCenter] postNotificationName:XVimBufferChangedNotification object:nil userInfo:userInfo]; } } } - (void)parseRcFile { - NSString *homeDir = NSHomeDirectoryForUser(NSUserName()); - NSString *keymapPath = [homeDir stringByAppendingString: @"/.xvimrc"]; + NSString *keymapPath = [@"~/.xvimrc" stringByExpandingTildeInPath]; NSString *keymapData = [[[NSString alloc] initWithContentsOfFile:keymapPath encoding:NSUTF8StringEncoding error:NULL] autorelease]; @@ -336,14 +353,17 @@ - (void)runTest:(id)sender{ [self.testRunner runTest]; } -- (void)toggleXVim:(id)sender{ - if( [(NSCell*)sender state] == NSOnState ){ - [DVTSourceTextViewHook unhook]; - [(NSCell*)sender setState:NSOffState]; - }else{ - [DVTSourceTextViewHook hook]; - [(NSCell*)sender setState:NSOnState]; +- (void)toggleXVim:(NSCell *)sender{ + if ([sender state] == NSOnState) { + _disabled = YES; + [sender setState:NSOffState]; + } else { + _disabled = NO; + [sender setState:NSOnState]; } + + [[NSNotificationCenter defaultCenter] postNotificationName:XVimEnabledStatusChangedNotification + object:nil userInfo:nil]; } @end diff --git a/XVim/XVimArgumentEvaluator.m b/XVim/XVimArgumentEvaluator.m index 7aaa8fd7..7df444a3 100644 --- a/XVim/XVimArgumentEvaluator.m +++ b/XVim/XVimArgumentEvaluator.m @@ -9,6 +9,7 @@ #import "XVimArgumentEvaluator.h" #import "XVimKeyStroke.h" #import "XVimWindow.h" +#import "XVimKeymapProvider.h" @implementation XVimArgumentEvaluator @synthesize keyStroke = _keyStroke; diff --git a/XVim/XVimBuffer.h b/XVim/XVimBuffer.h index 69005048..a13545c3 100644 --- a/XVim/XVimBuffer.h +++ b/XVim/XVimBuffer.h @@ -6,10 +6,364 @@ // // -#import "XVimTextStoring.h" +#import +#import "XVimDefs.h" -@interface XVimBuffer : NSObject +#pragma mark Macros -@property (nonatomic, readonly) XVimTextStorage *textStorage; +#ifdef DEBUG +// The methods here often take index as current interest position and index can be at EOF +// The following macros asserts the range of index. +// WITH_EOF permits the index at EOF position. +// WITHOUT_EOF doesn't permit the index at EOF position. +#define ASSERT_VALID_RANGE_WITH_EOF(x) NSAssert( x <= [self length], \ + @"index can not exceed the length of string. TextLength:%lu SpecifiedIndex:%lu", (long)[self length], x) +#else +#define ASSERT_VALID_RANGE_WITH_EOF(x) +#define ASSERT_VALID_CURSOR_POS(x) +#endif + +@class XVimUndoOperation, XVimBuffer; + +@interface NSTextStorage (XVimBuffer) +@property (nonatomic, readonly) XVimBuffer *xvim_buffer; +@end + +@interface NSDocument (XVimBuffer) +@property (nonatomic, readonly) XVimBuffer *xvim_buffer; +@end + +/** @brief class to represent an XVim Buffer + * + * If we were writing a vi clone, this would be the object owning the document + * and the text storage. But since XVim is meant to be a plugin to add vim + * bindings to existing apps like XCode, this is done in the reverse way. + * + * The XVimBuffer is an associated object of the NSDocument and NSTextStorage + * that the App we're hooking into is supposed to use. + * + * XVimBuffer supposes that there's a 1:1 mapping between the document + * and the storage and owns no reference to either of those. + * + * This means that when the document and textstorage get deallocated, + * either one may be stale, and make the app crash if we try to use them. + * + * FIXME: hook into NSDocument/NSTextStorage to invalidate + * the XVimBuffer in the rigth places. + * This isn't urgent though because IDE uses an NSDocument subclass + * that owns its textStorage, hence their lifetime is tied. + * + ****************************************************************************** + * + * Note that the terms here do not have the usual Cocoah meaning + * + * "Character" + * Character is a one unichar value. (any value including tabs,spaces) + * + * "index" + * This is the 0-based location of a given character within -xvim_string. + * + * "Position" + * This is an XVimPosition (line + column) + * + * "EOF" + * EOF is the position of the end of the document (-length), + * so for a text of "abc", EOF is just after the 'c', at position 3. + * + * What we have to think about is that a cursor can be at EOF, + * but -[xvim_string characterAtIndex:] at this position raises. + * + * We have to be careful about it when computing motions effects. + * + * "Newline" + * Newline is defined as "unichar determined by isNewline function". + * Usually "\n" or "\r". + * + * "Line" + * Line is a sequence of characters terminated by newline or EOF. + * "Line" includes the last newline character. + * Line numbers start at 1 + * + * "Blankline" + * Blankline is a line which has only newline or EOF. + * In other words, it is newline character or EOF after newline character. + * + * "Last of Line(LOL)" + * Last of line is the last character of a line EXCLUDING newline character. + * This means that blankline does NOT have an Last of line. + * + * "First of Line(FOL)" + * First of line is the first character of a line excluding newline character. + * This means that blankline does NOT have a First of line. + * + * "First Nonblank of Line" + * First Nonblank of Line is the first printable character in a line. + * + * "End of Line(EOL)" + * End of Line is newline or EOF character at the end of a line. + * A line always has an EOL. + * + * "Beginning of Line (BOL)" + * First character of a line including newline and EOF + * + */ + +@interface XVimBuffer : NSObject { +@public + XVimVisualInfo visualInfo; +} + +@property (nonatomic, readonly) NSDocument *document; +@property (nonatomic, readonly) NSTextStorage *textStorage; +@property (nonatomic, readonly) NSUndoManager *undoManager; + ++ (XVimBuffer *)makeBufferForDocument:(NSDocument *)document + textStorage:(NSTextStorage *)textStorage; + +#pragma mark Properties + +@property (nonatomic, readonly) NSString *string; +@property (nonatomic, readonly) NSString *lineEnding; +@property (nonatomic, readonly) NSUInteger numberOfLines; +@property (nonatomic, readonly) NSUInteger length; +@property (nonatomic, readonly) NSUInteger tabWidth; +@property (nonatomic, readonly) NSUInteger indentWidth; + +#pragma mark Specific positions of the index within the line + +/** @brief returns YES when \a index is on the last line + */ +- (BOOL)isIndexOnLastLine:(NSUInteger)index; + +/** @brief returns YES when \a index is at the beginning of its line + * It returns NO if \a index points to the \n of a \r\n + */ +- (BOOL)isIndexAtStartOfLine:(NSUInteger)index; + +/** @brief returns YES when \a index is at the end of its line + * It also returns YES if \a index points to the \n in a \r\n. + */ +- (BOOL)isIndexAtEndOfLine:(NSUInteger)index; + +/** @brief returns YES when \a index is a valid cursor position for normal mode + */ +- (BOOL)isNormalCursorPositionValidAtIndex:(NSUInteger)index; + +#pragma mark Converting between Indexes and Line Numbers + +/** @brief returns the index range for the given line number + * + * @param[in] num + * The line number + * @param[out] newLineLength + * The number of characters after the returned range forming the end of line + * @returns + * - {NSNotFound, 0} if the index is beyond the end of the document. + * - the range of indexes forming the line, excluding trailing newLine characters + */ +- (NSRange)indexRangeForLineNumber:(NSUInteger)num newLineLength:(NSUInteger *)newLineLength; + +/** @brief returns the index range for the given line range + * + * @param[in] range the line range. + * + * @returns + * the range of indexes forming the line, including trailing newLine characters + * Never returns NSNotFound + */ +- (NSRange)indexRangeForLines:(NSRange)range; + +/** @brief returns the line range around the given index + * + * @param[in] index + * The index within -xvim_string + * @param[out] newLineLength + * The number of characters after the returned range forming the end of line + * @returns + * the range of indexes forming the line, exclugint trailing newLine characters + * Note that if the index is within a CRLF for example, the range may end before index + */ +- (NSRange)indexRangeForLineAtIndex:(NSUInteger)index newLineLength:(NSUInteger *)newLineLength; + +/** @brief starting position of line @a num within -xvim_string. + * @returns the starting index for that line number or NSNotFound. + * @see -xvim_indexRangeForLineNumber:newLineLength: + */ +- (NSUInteger)indexOfLineNumber:(NSUInteger)num; + +/** @brief get the line number of a given position. + * + * @returns + * the line number of specified index. + * This never returns NSNotFound. + */ +- (NSUInteger)lineNumberAtIndex:(NSUInteger)index; + +#pragma mark Converting between Indexes and Line Numbers + Columns + +/** @brief returns the column number of \a index within the line. + * + * Column numbers starts at 0. + * + * This never returns NSNotFound. + */ +- (NSUInteger)columnOfIndex:(NSUInteger)index; + +/** @brief returns the XVim Posiion of \a index + * + * Column numbers starts at 0. + * + * This never returns NSNotFound. + */ +- (XVimPosition)positionOfIndex:(NSUInteger)index; + +/** @brief returns number of columns for the line containing \a index. + * + * If the specified line does not exist in the current document it returns NSNotFound + */ +- (NSUInteger)numberOfColumnsInLineAtIndex:(NSUInteger)index; + +/** @brief returns the index for the given line number and column. + * + * @returns + * NSNotFound if \a num exceeds the number of lines in the document + * If \a column is larger than the number of columns in that line, + * it returns the index of the endOfLine for that line + */ +- (NSUInteger)indexOfLineNumber:(NSUInteger)num column:(NSUInteger)column; + +/** @brief returns the index for the given line containing \a index and column. + * + * @returns + * Never returns NSNotfound + * If \a column is larger than the number of columns in that line, + * it returns the index of the endOfLine for that line + */ +- (NSUInteger)indexOfLineAtIndex:(NSUInteger)index column:(NSUInteger)column; + +#pragma mark Motion support functions + +/** @brief returns the index for a 'h/l' movement + * + * @param scount the motion count (negative means backwards) + * @param index the start index + * @param wrap wether the movement blocks at line start/end or wraps + * @returns + * This never returns NSNotFound, but it can return the end of line, + * and it's up to the caller to detect that if it's a problem. + */ +- (NSUInteger)indexOfCharMotion:(NSInteger)scount index:(NSUInteger)index options:(XVimMotionOptions)options; + +/** @brief returns the index for a 'j/k' movement + * + * @param scount the number of lines to go forward (negative means backward). + * @param index the line number containing that index is the starting line for that movement + * @param column the column to try to reach + * @returns + * this never returns NSNotFound, if the movement reaches the start + * or end of the file, the resulting line number is clamped. + */ +- (NSUInteger)indexOfLineMotion:(NSInteger)scount index:(NSUInteger)index column:(NSUInteger)column; + +#pragma mark Searching particular positions on the current line + +/** @brief position of the first character of the line containing \a index. + * + * @param index the index to search backwards from + */ +- (NSUInteger)startOfLine:(NSUInteger)index; // never returns NSNotFound + +/** @brief returns the firstOfLine for the line containing \a index. + * + * If the line is blank, this returns NSNotFound + * else this is the same as -beginningOfLine: + */ +- (NSUInteger)firstOfLine:(NSUInteger)index; // May return NSNotFound + +/** @brief position of the end of the line containing \a index. + * + * @param index the index to search from + * + * @returns + * the position of the end of the line. + * end of the line is either: + * - a newline character at the end of the line + * - end of the document + * + * Note that for files with \r\n if index points to \n + * this returns a position before index. + */ +- (NSUInteger)endOfLine:(NSUInteger)index; // never returns NSNotFound + +/** @brief returns the lastOfLine for the line containing \a index. + * + * If the line is blank, this returns NSNotFound + * else this is the same as -endOfLine:index - 1 + */ +- (NSUInteger)lastOfLine:(NSUInteger)index; // May return NSNotFound + +/** @brief returns the next non blank position on the same line. + * + * @param index the index to search from + * @param allowEOL whether reaching EOL is allowed or not + * + * @returns + * the position of the first non blank character, starting at index. + * + * if \a allowEOL is NO and that no non blank character is found, + * this returns NSNotFound. + */ +- (NSUInteger)nextNonblankInLineAtIndex:(NSUInteger)index allowEOL:(BOOL)allowEOL; + +/** @brief returns the first non blank character on the line, possibly EOL. + * + * @param index searches on the line containing that index. + * + * @returns + * the position of the first non blank character + * on the line containing \a index. + * + * if \a allowEOL is NO and that no non blank character is found, + * this returns NSNotFound. + */ +- (NSUInteger)firstNonblankInLineAtIndex:(NSUInteger)index allowEOL:(BOOL)allowEOL; + +/** @brief returns the next digit position on the same line. + * + * @param index the index to search from + * + * @returns + * the position of the first decimal digit character starting at \a index + * + * this returns NSNotFound if none is found. + */ +- (NSUInteger)nextDigitInLine:(NSUInteger)index; + +#pragma mark Support for modifications + +@property (nonatomic, readonly) BOOL isEditing; + +- (void)undoRedo:(XVimUndoOperation *)op; + +- (void)beginEditingAtIndex:(NSUInteger)index; +- (void)endEditingAtIndex:(NSUInteger)index; + +- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)string; +- (void)replaceCharactersInRange:(NSRange)range withSpaces:(NSUInteger)count; + +#define XVIM_BUFFER_SWAP_UPPER 0 +#define XVIM_BUFFER_SWAP_LOWER 1 +#define XVIM_BUFFER_SWAP_CASE 2 +#define XVIM_BUFFER_SWAP_ROT13 3 +- (void)swapCharactersInRange:(NSRange)range mode:(int)mode; + +// Never fails +- (NSUInteger)shiftLines:(XVimRange)lines column:(NSUInteger)column + count:(NSUInteger)count right:(BOOL)right block:(BOOL)block; + +// May return NSNotFound +- (NSUInteger)incrementNumberAtIndex:(NSUInteger)index by:(int64_t)offset; + +- (void)indentCharacterRange:(NSRange)range; @end diff --git a/XVim/XVimBuffer.m b/XVim/XVimBuffer.m index faf55e49..0f71b07c 100644 --- a/XVim/XVimBuffer.m +++ b/XVim/XVimBuffer.m @@ -6,10 +6,924 @@ // // +#import +#import "XVimStringBuffer.h" #import "XVimBuffer.h" +#import "XVimView.h" +#import "XVimUndo.h" +#import "XVimTextStoring.h" +#import "NSString+VimHelper.h" +#import "Logger.h" +#import "NSCharacterSet+XVimAdditions.h" + +static char const * const XVIM_KEY_BUFFER = "xvim_buffer"; + +@implementation NSTextStorage (XVimBuffer) + +- (XVimBuffer *)xvim_buffer +{ + return objc_getAssociatedObject(self, XVIM_KEY_BUFFER); +} + +@end + +@implementation NSDocument (XVimBuffer) + +- (XVimBuffer *)xvim_buffer +{ + return objc_getAssociatedObject(self, XVIM_KEY_BUFFER); +} + +@end @implementation XVimBuffer { + NSDocument *__unsafe_unretained _document; + NSTextStorage *__unsafe_unretained _textStorage; + XVimUndoOperation *_curOp; + NSUInteger _editCount; + NSString *_lineEnding; + + struct { + unsigned has_xvim_string : 1; + unsigned has_xvim_numberOfLines : 1; + unsigned has_xvim_tabWidth : 1; + unsigned has_xvim_indentWidth : 1; + unsigned has_xvim_indexRangeForLineNumber : 1; + unsigned has_xvim_indexRangeForLines : 1; + unsigned has_xvim_lineNumberAtIndex : 1; + } _flags; } +@synthesize document = _document; @synthesize textStorage = _textStorage; +- (NSUndoManager *)undoManager +{ + return _document.undoManager; +} + +- (instancetype)initWithDocument:(NSDocument *)document + textStorage:(NSTextStorage *)textStorage +{ + if ((self = [super init])) { + _document = document; + _textStorage = textStorage; + objc_setAssociatedObject(document, XVIM_KEY_BUFFER, self, OBJC_ASSOCIATION_RETAIN); + objc_setAssociatedObject(textStorage, XVIM_KEY_BUFFER, self, OBJC_ASSOCIATION_RETAIN); + + DEBUG_LOG("Buffer %p created for %@, backed by %@", self, document, textStorage); + + if ([_textStorage conformsToProtocol:@protocol(XVimTextStoring)]) { +#define CHECK(k, sel) _flags.has_xvim_##k = (bool)[_textStorage respondsToSelector:sel] + CHECK(string, @selector(xvim_string)); + CHECK(numberOfLines, @selector(xvim_numberOfLines)); + CHECK(tabWidth, @selector(xvim_tabWidth)); + CHECK(indentWidth, @selector(xvim_indentWidth)); + CHECK(indexRangeForLineNumber, @selector(xvim_indexRangeForLineNumber:newLineLength:)); + CHECK(indexRangeForLines, @selector(xvim_indexRangeForLines:)); + CHECK(lineNumberAtIndex, @selector(xvim_lineNumberAtIndex:)); +#undef CHECK + } + } + return self; +} + +- (void)dealloc +{ + DEBUG_LOG("Buffer %p destroyed", self); + + [_curOp release]; + [super dealloc]; +} + ++ (XVimBuffer *)makeBufferForDocument:(NSDocument *)document + textStorage:(NSTextStorage *)textStorage +{ + return [[[[self class] alloc] initWithDocument:document textStorage:textStorage] autorelease]; +} + +#pragma mark Properties +#define _XVimTextStorage ((NSTextStorage *)_textStorage) + +- (NSString *)string +{ + if (_flags.has_xvim_string) { + return _XVimTextStorage.xvim_string; + } + return _textStorage.string; +} + +- (NSString *)lineEnding +{ + if (!_lineEnding) { + NSUInteger nlLen; + NSRange r; + + r = [self indexRangeForLineAtIndex:0 newLineLength:&nlLen]; + if (nlLen == 2) { + _lineEnding = @"\r\n"; + } else if (nlLen == 1) { + unichar c = [self.string characterAtIndex:NSMaxRange(r)]; + if (c == '\n') { + _lineEnding = @"\n"; + } else if (c == '\r') { + _lineEnding = @"\r"; + } else { + // WTF?? + return @"\n"; + } + } else { + // FIXME: use the setting + return @"\n"; + } + } + return _lineEnding; +} + +- (NSUInteger)numberOfLines +{ + if (_flags.has_xvim_numberOfLines) { + return _XVimTextStorage.xvim_numberOfLines; + } + return [self lineNumberAtIndex:self.length]; +} + +- (NSUInteger)length +{ + return _textStorage.length; +} + +- (NSUInteger)tabWidth +{ + if (_flags.has_xvim_tabWidth) { + return _XVimTextStorage.xvim_tabWidth; + } + return 8; +} + +- (NSUInteger)indentWidth +{ + if (_flags.has_xvim_indentWidth) { + return _XVimTextStorage.xvim_indentWidth; + } + return 8; +} + +#pragma mark Specific positions of the index within the line + +NS_INLINE NSUInteger _addClamp(NSUInteger a, NSInteger b, NSUInteger min, NSUInteger max) +{ +#if DEBUG + NSCAssert(min <= a && a <= max, @"invalid parameter to _addClamp"); +#endif + if (b < 0 && (NSInteger)a + b <= min) { + return min; + } + if (b > 0 && a + (NSUInteger)b > max) { + return max; + } + return (NSUInteger)((NSInteger)a + b); +} + +- (BOOL)isIndexOnLastLine:(NSUInteger)index +{ + return [self endOfLine:index] == self.length; +} + +- (BOOL)isIndexAtStartOfLine:(NSUInteger)index +{ + if (index == 0) { + return YES; + } + if (!isNewline([self.string characterAtIndex:index - 1])) { + return NO; + } + if (index >= self.length || !isNewline([self.string characterAtIndex:index])) { + return YES; + } + return [self startOfLine:index] == index; +} + +- (BOOL)isIndexAtEndOfLine:(NSUInteger)index +{ + return index >= self.length || isNewline([self.string characterAtIndex:index]); +} + +- (BOOL)isNormalCursorPositionValidAtIndex:(NSUInteger)index +{ + NSRange range = [self indexRangeForLineAtIndex:index newLineLength:NULL]; + + return range.length == 0 || index < NSMaxRange(range); +} + +#pragma mark Converting between Indexes and Line Numbers + +- (NSRange)indexRangeForLineNumber:(NSUInteger)num newLineLength:(NSUInteger *)newLineLength +{ + NSAssert(num > 0, @"line number starts at 1"); + + if (_flags.has_xvim_indexRangeForLineNumber) { + return [_XVimTextStorage xvim_indexRangeForLineNumber:num newLineLength:newLineLength]; + } + + // TODO: we may need to keep track line number and position by hooking insertText: method. + // FIXME: this code is actually never called in XVim for XCode, it probably has bugs, it's not tested + + NSString *string = self.string; + NSUInteger length = self.length; + NSUInteger lineNum = 0, end = 0, pos, contentsEnd; + + do { + pos = end; + [string getLineStart:NULL end:&end contentsEnd:&contentsEnd forRange:NSMakeRange(end, 0)]; + lineNum++; + if (lineNum == num) { + if (newLineLength) *newLineLength = end - contentsEnd; + return NSMakeRange(pos, contentsEnd - pos); + } + } while (end < length); + + if (newLineLength) *newLineLength = 0; + + // we have a last empty line after \n + if (contentsEnd < end) { + lineNum++; + if (lineNum == num) { + return NSMakeRange(end, 0); + } + } + + return NSMakeRange(NSNotFound, 0); +} + +- (NSRange)indexRangeForLines:(NSRange)range +{ + NSAssert(range.location > 0, @"line number starts at 1"); + + if (_flags.has_xvim_indexRangeForLines) { + return [_XVimTextStorage xvim_indexRangeForLines:range]; + } + + // TODO: we may need to keep track line number and position by hooking insertText: method. + // FIXME: this code is actually never called in XVim for XCode, it probably has bugs, it's not tested + NSString *string = self.string; + NSUInteger length = self.length, start = 0; + NSUInteger lineNum = 0, end = 0, contentsEnd; + + do { + [string getLineStart:NULL end:&end contentsEnd:&contentsEnd forRange:NSMakeRange(end, 0)]; + lineNum++; + if (lineNum == range.location) { + start = end; + } + if (lineNum == NSMaxRange(range)) { + return NSMakeRange(start, end - start); + } + } while (end < length); + + // we have a last empty line after \n + if (contentsEnd < end) { + lineNum++; + if (lineNum == range.location) { + start = end; + } + if (lineNum == NSMaxRange(range)) { + return NSMakeRange(start, end - start); + } + } + + return NSMakeRange(0, length); +} + +- (NSRange)indexRangeForLineAtIndex:(NSUInteger)index newLineLength:(NSUInteger *)newLineLength +{ + ASSERT_VALID_RANGE_WITH_EOF(index); + NSString *string = self.string; + NSUInteger len = self.length; + NSUInteger end, contentEnd; + + if (index > len) { + index = len; + } + + [string getLineStart:&index end:&end contentsEnd:&contentEnd forRange:NSMakeRange(index, 0)]; + if (newLineLength) *newLineLength = end - contentEnd; + return NSMakeRange(index, contentEnd - index); +} + +- (NSUInteger)indexOfLineNumber:(NSUInteger)num +{ + NSAssert(num > 0, @"line number start at 1"); + if (num == 1) { + return 0; + } + return [self indexRangeForLineNumber:num newLineLength:NULL].location; +} + +- (NSUInteger)lineNumberAtIndex:(NSUInteger)index +{ + ASSERT_VALID_RANGE_WITH_EOF(index); + + if (_flags.has_xvim_lineNumberAtIndex) { + return [_XVimTextStorage xvim_lineNumberAtIndex:index]; + } + + NSString *string = self.string; + NSUInteger len = self.length; + NSUInteger num = 1, pos = 0; + + if (index > len) { + index = len; + } + + do { + num++; + if (index == pos) { + return num; + } + [string getLineStart:NULL end:&pos contentsEnd:NULL forRange:NSMakeRange(pos, 0)]; + } while (pos < index); + + return num; +} + + +#pragma mark Converting between Indexes and Line Numbers + Columns + +static NSUInteger xvim_sb_count_columns(xvim_string_buffer_t *sb, NSUInteger tabWidth) +{ + NSUInteger col = 0; + + if (!xvim_sb_at_end(sb)) { + do { + if (xvim_sb_peek(sb) == '\t') { + col += tabWidth; + if (tabWidth) col -= col % tabWidth; + } else { + col++; + } + } while (xvim_sb_next(sb)); + } + + return col; +} + +- (NSUInteger)columnOfIndex:(NSUInteger)index +{ + NSRange range = [self indexRangeForLineAtIndex:index newLineLength:NULL]; + xvim_string_buffer_t sb; + + if (index < NSMaxRange(range)) { + range.length = index - range.location; + } + if (range.length == 0) { + return 0; + } + + xvim_sb_init_range(&sb, self.string, range); + return xvim_sb_count_columns(&sb, self.tabWidth); +} + +- (XVimPosition)positionOfIndex:(NSUInteger)index +{ + return XVimMakePosition([self lineNumberAtIndex:index], [self columnOfIndex:index]); +} + +- (NSUInteger)numberOfColumnsInLineAtIndex:(NSUInteger)index +{ + NSRange range = [self indexRangeForLineAtIndex:index newLineLength:NULL]; + xvim_string_buffer_t sb; + + xvim_sb_init_range(&sb, self.string, range); + return xvim_sb_count_columns(&sb, self.tabWidth); +} + +- (NSUInteger)_indexOfLineAtIndex:(NSUInteger)index column:(NSUInteger)column +{ + if (column == 0) { + return index; + } + + NSRange range = [self indexRangeForLineAtIndex:index newLineLength:NULL]; + NSUInteger tabWidth = self.tabWidth; + NSUInteger col = 0; + xvim_string_buffer_t sb; + + xvim_sb_init_range(&sb, self.string, range); + do { + if (xvim_sb_peek(&sb) == '\t') { + col += tabWidth; + if (tabWidth) col -= col % tabWidth; + } else { + col++; + } + if (col > column) { + return xvim_sb_index(&sb); + } + } while (xvim_sb_next(&sb) && col < column); + + return xvim_sb_index(&sb); +} + +- (NSUInteger)indexOfLineAtIndex:(NSUInteger)index column:(NSUInteger)column +{ + return [self _indexOfLineAtIndex:[self startOfLine:index] column:column]; +} + +- (NSUInteger)indexOfLineNumber:(NSUInteger)num column:(NSUInteger)column +{ + NSUInteger index = [self indexOfLineNumber:num]; + + if (column == 0 || index == NSNotFound) { + return index; + } + + return [self _indexOfLineAtIndex:index column:column]; +} + +#pragma mark Motion support functions + +- (NSUInteger)indexOfCharMotion:(NSInteger)scount index:(NSUInteger)index options:(XVimMotionOptions)options +{ + NSUInteger nlLen, max; + NSRange range; + + NSAssert((options & ~MOPT_NOWRAP) == 0, @"unhandled options passed"); + + if (scount == 0) { + return index; + } + + if (options & MOPT_NOWRAP) { + range = [self indexRangeForLineAtIndex:index newLineLength:&nlLen]; + return _addClamp(index, scount, range.location, NSMaxRange(range)); + } + + while (scount > 0) { + range = [self indexRangeForLineAtIndex:index newLineLength:&nlLen]; + max = NSMaxRange(range); + if (index + (NSUInteger)scount > max) { + scount -= max - index + 1; + index = max + nlLen; + } else { + return index + (NSUInteger)scount; + } + } + + while (scount < 0) { + range = [self indexRangeForLineAtIndex:index - 1 newLineLength:&nlLen]; + max = NSMaxRange(range); + if (index > max) { + // if we're just past the EOL skip it back + scount++; + index = max; + } + if ((index - range.location) >= (NSUInteger)-scount) { + return (NSUInteger)((NSInteger)index + scount); + } + scount += index - range.location; + index = range.location; + } + + return index; +} + +- (NSUInteger)indexOfLineMotion:(NSInteger)scount index:(NSUInteger)index column:(NSUInteger)column; +{ + if (scount) { + NSUInteger line = [self lineNumberAtIndex:index]; + + line = _addClamp(line, scount, 1, self.numberOfLines); + index = [self indexOfLineNumber:line]; + } + + return [self _indexOfLineAtIndex:index column:column]; +} + +#pragma mark Searching particular positions on the current line + +- (NSUInteger)startOfLine:(NSUInteger)index +{ + ASSERT_VALID_RANGE_WITH_EOF(index); + NSString *string = self.string; + NSUInteger len = self.length; + + if (index > len) { + index = len; + } + [string getLineStart:&index end:NULL contentsEnd:NULL forRange:NSMakeRange(index, 0)]; + return index; +} + +- (NSUInteger)firstOfLine:(NSUInteger)index +{ + NSUInteger pos = [self startOfLine:index]; + + if (pos == index && isNewline([self.string characterAtIndex:pos])) { + return NSNotFound; + } + return pos; +} + +- (NSUInteger)endOfLine:(NSUInteger)index +{ + ASSERT_VALID_RANGE_WITH_EOF(index); + NSString *string = self.string; + NSUInteger len = self.length; + + if (index > len) { + index = len; + } + [string getLineStart:NULL end:NULL contentsEnd:&index forRange:NSMakeRange(index, 0)]; + return index; +} + +- (NSUInteger)lastOfLine:(NSUInteger)index +{ + NSUInteger pos = [self endOfLine:index]; + + if (pos <= index && (pos == 0 || isNewline([self.string characterAtIndex:pos - 1]))) { + return NSNotFound; + } + return pos - 1; +} + +- (NSUInteger)nextNonblankInLineAtIndex:(NSUInteger)index allowEOL:(BOOL)allowEOL +{ + NSString *s = self.string; + xvim_string_buffer_t sb; + unichar c; + + ASSERT_VALID_RANGE_WITH_EOF(index); + + xvim_sb_init(&sb, s, index, index, s.length); + xvim_sb_skip_forward(&sb, [NSCharacterSet whitespaceCharacterSet]); + c = xvim_sb_peek(&sb); + + if (!allowEOL && (c == XVimInvalidChar || isNewline(c))) { + return NSNotFound; + } + return xvim_sb_index(&sb); +} + +- (NSUInteger)firstNonblankInLineAtIndex:(NSUInteger)index allowEOL:(BOOL)allowEOL +{ + index = [self startOfLine:index]; + return [self nextNonblankInLineAtIndex:index allowEOL:allowEOL]; +} + +- (NSUInteger)nextDigitInLine:(NSUInteger)index +{ + xvim_string_buffer_t sb; + xvim_sb_init(&sb, self.string, index, index, [self endOfLine:index]); + + if (xvim_sb_find_forward(&sb, [NSCharacterSet decimalDigitCharacterSet])) { + return xvim_sb_index(&sb); + } + + return NSNotFound; +} + +#pragma mark Support for modifications + +- (BOOL)isEditing +{ + return _editCount > 0; +} + +- (void)undoRedo:(XVimUndoOperation *)op +{ + for (NSLayoutManager *mgr in _textStorage.layoutManagers) { + NSTextView *view = mgr.firstTextView; + + if (view.textStorage == _textStorage) { + [op undoRedo:self view:view.xvim_view]; + return; + } + } + + [op undoRedo:self view:nil]; +} + +- (void)beginEditingAtIndex:(NSUInteger)index +{ + NSAssert(!_curOp || _editCount, @"invalid undo state"); + if (_curOp) { + _editCount++; + _curOp.startIndex = index; + } else { + _curOp = [[XVimUndoOperation alloc] initWithIndex:index]; + _editCount = 1; + } +} + +- (void)endEditingAtIndex:(NSUInteger)index +{ + _curOp.endIndex = index; + + if (--_editCount == 0) { + [_curOp registerForBuffer:self]; + [_curOp release]; + _curOp = nil; + } +} + +- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)string +{ + NSAssert(_curOp, @"you must call -beginEditingAtIndex: first"); + [_curOp addUndoRange:range replacementRange:NSMakeRange(range.location, string.length) buffer:self]; + [_textStorage replaceCharactersInRange:range withString:string]; +} + +- (void)replaceCharactersInRange:(NSRange)range withSpaces:(NSUInteger)count +{ + if (count == 0) { + [self replaceCharactersInRange:range withString:@""]; + } else if (range.length) { + [self replaceCharactersInRange:range withString:[NSString stringMadeOfSpaces:count]]; + } +} + +NS_INLINE unichar rot13(unichar c) +{ + switch (c) { + case 'a' ... 'a' + 12: + case 'A' ... 'A' + 12: + return c + 13; + case 'z' - 12 ... 'z': + case 'Z' - 12 ... 'Z': + return c - 13; + } + return c; +} + +- (void)swapCharactersInRange:(NSRange)range mode:(int)mode +{ + NSMutableString *s, *L, *U; + unichar buf[XVIM_STRING_BUFFER_SIZE]; + unsigned pos = 0; + xvim_string_buffer_t sb; + + NSAssert(_curOp, @"you must call -beginEditingAtIndex: first"); + if (!range.length) { + return; + } + + CFLocaleRef locale = CFLocaleCopyCurrent(); + + if (mode == XVIM_BUFFER_SWAP_UPPER || mode == XVIM_BUFFER_SWAP_LOWER) { + s = [self.string newMutableSubstringWithRange:range]; + if (mode == XVIM_BUFFER_SWAP_LOWER) { + CFStringLowercase((CFMutableStringRef)s, locale); + } else { + CFStringUppercase((CFMutableStringRef)s, locale); + } + } else if (mode == XVIM_BUFFER_SWAP_ROT13) { + xvim_sb_init_range(&sb, self.string, range); + s = [[NSMutableString alloc] initWithCapacity:range.length]; + + do { + buf[pos++] = rot13(xvim_sb_peek(&sb)); + if (pos >= XVIM_STRING_BUFFER_SIZE) { + [s appendCharacters:buf length:pos]; + pos = 0; + } + } while (xvim_sb_next(&sb)); + if (pos) { + [s appendCharacters:buf length:pos]; + } + } else { + xvim_sb_init_range(&sb, self.string, range); + s = [[NSMutableString alloc] initWithCapacity:range.length]; + L = [[NSMutableString alloc] initWithCapacity:1]; + U = [[NSMutableString alloc] initWithCapacity:1]; + + do { + unichar ch = xvim_sb_peek(&sb); + + [L replaceCharactersInRange:NSMakeRange(0, L.length) withString:@""]; + [L appendCharacters:&ch length:1]; + [U replaceCharactersInRange:NSMakeRange(0, U.length) withString:@""]; + [U appendCharacters:&ch length:1]; + + CFStringUppercase((CFMutableStringRef)U, locale); + if ([L isEqualToString:U]) { + CFStringLowercase((CFMutableStringRef)L, locale); + [s appendString:L]; + } else { + [s appendString:U]; + } + } while (xvim_sb_next(&sb)); + + [L release]; + [U release]; + } + + [self replaceCharactersInRange:range withString:s]; + [s release]; + CFRelease(locale); +} + +- (void)_removeSpacesAtLine:(NSUInteger)line column:(NSUInteger)column count:(NSUInteger)count +{ + NSUInteger tabWidth = self.tabWidth; + NSUInteger spaces = 0, width = 0, start; + xvim_string_buffer_t sb; + + start = [self indexOfLineNumber:line column:column]; + + xvim_sb_init(&sb, self.string, start, start, self.length); + if (xvim_sb_peek(&sb) == '\t') { + spaces = column - [self columnOfIndex:start]; + } else if (xvim_sb_peek(&sb) != ' ') { + return; + } + + while (width < count) { + unichar c = xvim_sb_peek(&sb); + + if (c != ' ' && c != '\t') { + break; + } + if (c == '\t') { + width += tabWidth - ((column + width) % tabWidth); + } else { + width++; + } + + if (!xvim_sb_next(&sb)) { + break; + } + } + + if (width > count) { + spaces += width - count; + } + + NSRange range = xvim_sb_range_to_start(&sb); + [self replaceCharactersInRange:range withString:[NSString stringMadeOfSpaces:spaces]]; +} + +- (NSUInteger)shiftLines:(XVimRange)lines column:(NSUInteger)column + count:(NSUInteger)count right:(BOOL)right block:(BOOL)blockMode +{ + NSString *string = self.string; + + if (right) { + NSString *s = [NSString stringMadeOfSpaces:count]; + NSUInteger tabWidth = self.tabWidth; + + for (NSUInteger line = lines.begin; line <= lines.end; line++) { + NSUInteger index, spaces = 0; + + index = [self indexOfLineNumber:line column:column]; + if (index >= self.length || isNewline([string characterAtIndex:index])) { + if (column == 0 || [self columnOfIndex:column] < column) { + continue; + } + } + + if (tabWidth && [string characterAtIndex:index] == '\t') { + NSUInteger col = [self columnOfIndex:index]; + + spaces = tabWidth - (col % tabWidth); + } + + if (spaces) { + NSString *s2 = [NSString stringMadeOfSpaces:count + spaces]; + [self replaceCharactersInRange:NSMakeRange(index, 1) withString:s2]; + } else { + [self replaceCharactersInRange:NSMakeRange(index, 0) withString:s]; + } + } + } else { + for (NSUInteger line = lines.begin; line <= lines.end; line++) { + [self _removeSpacesAtLine:line column:column count:count]; + } + } + + NSUInteger pos; + if (blockMode) { + pos = [self indexOfLineNumber:lines.begin column:column]; + } else { + pos = [self indexOfLineNumber:lines.begin]; + pos = [self firstNonblankInLineAtIndex:pos allowEOL:YES]; + } + return pos; +} + +- (NSRange)_numberAtIndex:(NSUInteger)index +{ + NSUInteger o_start, n_start, x_start; + NSUInteger o_end, n_end, x_end; + NSString *s = self.string; + unichar c; + BOOL isOctal = YES; + xvim_string_buffer_t sb; + + xvim_sb_init(&sb, s, index, 0, s.length); + xvim_sb_skip_forward(&sb, [NSCharacterSet xvim_octDigitsCharacterSet]); + o_end = xvim_sb_index(&sb); + xvim_sb_skip_forward(&sb, [NSCharacterSet decimalDigitCharacterSet]); + n_end = xvim_sb_index(&sb); + if (o_end != n_end) isOctal = NO; + xvim_sb_skip_forward(&sb, [NSCharacterSet xvim_hexDigitsCharacterSet]); + x_end = xvim_sb_index(&sb); + + xvim_sb_init(&sb, s, index, 0, s.length); + if (isOctal) { + xvim_sb_skip_backward(&sb, [NSCharacterSet xvim_octDigitsCharacterSet]); + o_start = xvim_sb_index(&sb); + c = xvim_sb_peek_prev(&sb); + if (c == '8' || c == '9' || xvim_sb_peek(&sb) != '0') { + isOctal = NO; + } + } + xvim_sb_skip_backward(&sb, [NSCharacterSet decimalDigitCharacterSet]); + n_start = xvim_sb_index(&sb); + xvim_sb_skip_backward(&sb, [NSCharacterSet xvim_hexDigitsCharacterSet]); + x_start = xvim_sb_index(&sb); + + // first deal with Hex: 0xNNNNN + // case 1: check for insertion point on the '0' or 'x' + if (x_end - x_start == 1) { + NSUInteger end = x_end; + if (end < s.length && [s characterAtIndex:end] == 'x') { + xvim_sb_init(&sb, s, end + 1, 0, s.length); + xvim_sb_skip_forward(&sb, [NSCharacterSet xvim_hexDigitsCharacterSet]); + end = xvim_sb_index(&sb); + + if (index < end && end - x_start > 2) { + // YAY it's hex for real!!! + return NSMakeRange(x_start, end - x_start); + } + } + } + + // case 2: check whether we're after 0x + if (index < x_end && x_end - x_start >= 1) { + if (x_start >= 2 && [s characterAtIndex:x_start - 1] == 'x' && [s characterAtIndex:x_start - 2] == '0') { + return NSMakeRange(x_start - 2, x_end - x_start + 2); + } + } + + if (index == n_end || n_start - n_end == 0) { + return NSMakeRange(NSNotFound, 0); + } + + // okay it's not hex, if it's not octal, check for leading +/- + if (n_start > 0 && !isOctal) { + c = [s characterAtIndex:n_start - 1]; + if (c == '+' || c == '-') { + n_start--; + } + } + return NSMakeRange(n_start, n_end - n_start); +} + +- (NSUInteger)incrementNumberAtIndex:(NSUInteger)index by:(int64_t)offset +{ + NSRange range; + + range = [self _numberAtIndex:index]; + if (range.location == NSNotFound) { + NSUInteger pos = [self nextDigitInLine:index]; + if (pos == NSNotFound) { + return NSNotFound; + } + range = [self _numberAtIndex:pos]; + if (range.location == NSNotFound) { + // should not happen + return NSNotFound; + } + } + + const char *s = [[self.string substringWithRange:range] UTF8String]; + NSString *repl; + uint64_t u = strtoull(s, NULL, 0); + int64_t i = strtoll(s, NULL, 0); + + if (strncmp(s, "0x", 2) == 0) { + repl = [NSString stringWithFormat:@"0x%0*llx", (int)strlen(s) - 2, u + (uint64_t)offset]; + } else if (u && *s == '0' && s[1] && !strchr(s, '9') && !strchr(s, '8')) { + repl = [NSString stringWithFormat:@"0%0*llo", (int)strlen(s) - 1, u + (uint64_t)offset]; + } else if (u && *s == '+') { + repl = [NSString stringWithFormat:@"%+lld", i + offset]; + } else { + repl = [NSString stringWithFormat:@"%lld", i + offset]; + } + + [self beginEditingAtIndex:index]; + [self replaceCharactersInRange:range withString:repl]; + index = range.location + repl.length - 1; + [self endEditingAtIndex:index]; + return index; +} + +- (void)indentCharacterRange:(NSRange)range +{ + if ([_textStorage respondsToSelector:@selector(xvim_indentCharacterRange:buffer:)]) { + [(id)_textStorage xvim_indentCharacterRange:range buffer:self]; + } +} + @end diff --git a/XVim/XVimCommandLineEvaluator.m b/XVim/XVimCommandLineEvaluator.m index ce5dc199..a000c6d6 100644 --- a/XVim/XVimCommandLineEvaluator.m +++ b/XVim/XVimCommandLineEvaluator.m @@ -8,7 +8,7 @@ #import "Logger.h" #import "XVimCommandLineEvaluator.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "XVimKeymapProvider.h" #import "XVimWindow.h" #import "XVimCommandField.h" @@ -28,6 +28,8 @@ @interface XVimCommandLineEvaluator() { @end @implementation XVimCommandLineEvaluator +@synthesize evalutionResult = _evalutionResult; +@synthesize lastTextView = _lastTextView; - (id)initWithWindow:(XVimWindow *)window firstLetter:(NSString*)firstLetter @@ -42,7 +44,7 @@ - (id)initWithWindow:(XVimWindow *)window _onKeyPress = [keyPressHandler copy]; _historyNo = 0; _evalutionResult = nil; - self.lastTextView = window.sourceView; + self.lastTextView = window.currentView.textView; XVimCommandField *commandField = self.window.commandLine.commandField; [commandField setString:_firstLetter]; [commandField moveToEndOfLine:self]; @@ -94,13 +96,13 @@ - (XVimEvaluator*)execute{ - (void)takeFocusFromWindow{ XVimCommandField *commandField = self.window.commandLine.commandField; [commandField setDelegate:self.window]; - [[[self.window sourceView] window] makeFirstResponder:commandField]; + [self.lastTextView.window makeFirstResponder:commandField]; } - (void)relinquishFocusToWindow{ XVimCommandField *commandField = self.window.commandLine.commandField; [commandField setDelegate:nil]; - [[self.lastTextView window] makeFirstResponder:self.lastTextView]; + [self.lastTextView.window makeFirstResponder:self.lastTextView]; [commandField setHidden:YES]; } @@ -108,8 +110,9 @@ - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ XVimEvaluator* next = self; XVimCommandField *commandField = self.window.commandLine.commandField; - if ([keyStroke instanceResponds:self]) { - next = [self performSelector:[keyStroke selector]]; + SEL sel = keyStroke.selector; + if ([self respondsToSelector:sel]) { + next = [self performSelector:sel]; } else{ [commandField handleKeyStroke:keyStroke inWindow:self.window]; @@ -138,7 +141,7 @@ - (XVimEvaluator*)defaultNextEvaluatorInWindow:(XVimWindow*)window{ return nil; } -- (float)insertionPointHeightRatio{ +- (CGFloat)insertionPointHeightRatio{ return 0.0; } @@ -167,8 +170,7 @@ - (XVimEvaluator*)CR{ } - (XVimEvaluator*)ESC{ - NSTextView *sourceView = [self sourceView]; - [sourceView xvim_scrollTo:[sourceView insertionPoint]]; + [self.currentView scrollTo:self.currentView.insertionPoint]; return nil; } diff --git a/XVim/XVimDebug.m b/XVim/XVimDebug.m index 9e341191..e6b04c05 100644 --- a/XVim/XVimDebug.m +++ b/XVim/XVimDebug.m @@ -27,18 +27,22 @@ - (void)trace:(NSArray*)params withWindow:(XVimWindow*)window{ } } -- (void)highlight:(NSArray*)params withWindow:(XVimWindow*)window{ - NSTextView* view = window.sourceView; - [[view textStorage] beginEditing]; - [[view textStorage] addAttribute:NSBackgroundColorAttributeName value:[NSColor yellowColor] range:NSMakeRange(0,5)]; - [[view textStorage] endEditing]; +- (void)highlight:(NSArray*)params withWindow:(XVimWindow*)window +{ + NSTextStorage *ts = window.currentBuffer.textStorage; + + [ts beginEditing]; + [ts addAttribute:NSBackgroundColorAttributeName value:[NSColor yellowColor] range:NSMakeRange(0,5)]; + [ts endEditing]; } -- (void)highlightclear:(NSArray*)params withWindow:(XVimWindow*)window{ - NSTextView* view = window.sourceView; - [[view textStorage] beginEditing]; - [[view textStorage] addAttribute:NSBackgroundColorAttributeName value:[NSColor clearColor] range:NSMakeRange(0, view.string.length)]; - [[view textStorage] endEditing]; +- (void)highlightclear:(NSArray*)params withWindow:(XVimWindow*)window +{ + NSTextStorage *ts = window.currentBuffer.textStorage; + + [ts beginEditing]; + [ts addAttribute:NSBackgroundColorAttributeName value:[NSColor clearColor] range:NSMakeRange(0, ts.length)]; + [ts endEditing]; } @end diff --git a/XVim/XVimDefs.h b/XVim/XVimDefs.h index a80f8fde..999ae022 100644 --- a/XVim/XVimDefs.h +++ b/XVim/XVimDefs.h @@ -19,16 +19,43 @@ #endif #endif -typedef NS_ENUM(NSUInteger, XVimInsertionPoint) { +typedef NS_ENUM(uint8_t, XVimInsertionPoint) { XVIM_INSERT_DEFAULT, - XVIM_INSERT_SPACES, XVIM_INSERT_APPEND, XVIM_INSERT_BEFORE_FIRST_NONBLANK, XVIM_INSERT_APPEND_EOL, XVIM_INSERT_BLOCK_KILL, - XVIM_INSERT_BLOCK_KILL_EOL, }; +typedef NS_ENUM(uint8_t, XVimSortOptions) { + XVimSortOptionReversed = 1, + XVimSortOptionRemoveDuplicateLines = 1 << 1, + XVimSortOptionNumericSort = 1 << 2, + XVimSortOptionIgnoreCase = 1 << 3 +}; + +typedef NS_OPTIONS(unsigned , XVimMotionOptions) { + MOPT_NONE = 0x00, + MOPT_NOWRAP = 0x01, // whether we stop or wrap at EOL + MOPT_BIGWORD = 0x02, // for 'WORD' motion + MOPT_PARA_BOUND_BLANKLINE = 0x04, + MOPT_TEXTOBJECT_INNER = 0x08, + MOPT_SEARCH_WRAP = 0x10, + MOPT_SEARCH_CASEINSENSITIVE = 0x20, + MOPT_CHANGE_WORD = 0x40, // for 'cw','cW' +}; + +typedef enum{ + TEXT_TYPE_CHARACTERS, + TEXT_TYPE_BLOCK, + TEXT_TYPE_LINES +} TEXT_TYPE; + +typedef enum { + CURSOR_MODE_COMMAND, // default must be command + CURSOR_MODE_INSERT, +} CURSOR_MODE; + typedef enum { XVIM_MODE_NONE, XVIM_MODE_NORMAL, @@ -40,12 +67,12 @@ typedef enum { XVIM_MODE_COUNT, // This is the count of modes } XVIM_MODE; -typedef enum { +typedef NS_ENUM(uint8_t, XVimVisualMode) { XVIM_VISUAL_NONE, XVIM_VISUAL_CHARACTER, // for 'v' XVIM_VISUAL_LINE, // for 'V' XVIM_VISUAL_BLOCK, // for 'CTRL-V' -}XVIM_VISUAL_MODE; +}; typedef enum { _XVIM_VISUAL_RIGHT = 1, @@ -75,8 +102,28 @@ typedef struct _XVimSelection { NSUInteger right; } XVimSelection; +typedef struct { + XVimVisualMode mode; + NSUInteger colwant; + XVimPosition start; + XVimPosition end; +} XVimVisualInfo; + #define XVimSelectionEOL (NSIntegerMax - 1) +NS_INLINE NSUInteger XVimVisualInfoColumns(XVimVisualInfo *vi) +{ + if (vi->end.column == XVimSelectionEOL) { + return XVimSelectionEOL; + } + return MAX(vi->end.column, vi->start.column) - MIN(vi->end.column, vi->start.column) + 1; +} + +NS_INLINE NSUInteger XVimVisualInfoLines(XVimVisualInfo *vi) +{ + return MAX(vi->end.line, vi->start.line) - MIN(vi->end.line, vi->start.line) + 1; +} + NS_INLINE XVimRange XVimMakeRange(NSUInteger begin, NSUInteger end) { XVimRange r; r.begin = begin; diff --git a/XVim/XVimDeleteEvaluator.m b/XVim/XVimDeleteEvaluator.m index 65a16613..7ff276cd 100644 --- a/XVim/XVimDeleteEvaluator.m +++ b/XVim/XVimDeleteEvaluator.m @@ -9,7 +9,7 @@ #import "XVimDeleteEvaluator.h" #import "XVimInsertEvaluator.h" #import "XVimWindow.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "XVimTextObjectEvaluator.h" #import "Logger.h" #import "XVim.h" @@ -40,7 +40,7 @@ - (XVimEvaluator*)c{ if ([self numericArg] < 1) return nil; - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg]-1); + XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOPT_NONE, [self numericArg]-1); return [self _motionFixed:m]; } @@ -54,7 +54,7 @@ - (XVimEvaluator*)d{ if ([self numericArg] < 1) return nil; - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg]-1); + XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOPT_NONE, [self numericArg]-1); return [self _motionFixed:m]; } @@ -70,13 +70,14 @@ -(XVimEvaluator*)motionFixed:(XVimMotion*)motion{ if (_insertModeAtCompletion == TRUE) { // Do not repeat the insert, that is how vim works so for // example 'c3wWord' results in Word not WordWordWord - [[self sourceView] xvim_change:motion]; + [self.currentView doChange:motion]; [self resetNumericArg]; // Do not call [[XVim instance] fixRepeatCommand] here. // It will be called after XVimInsertEvaluator finish handling key input. return [[[XVimInsertEvaluator alloc] initWithWindow:self.window] autorelease]; - }else{ - [[self sourceView] xvim_delete:motion andYank:YES]; + } else { + [self.currentView doDelete:motion andYank:YES]; + [self.currentView adjustCursorPosition]; } return nil; } diff --git a/XVim/XVimEqualEvaluator.m b/XVim/XVimEqualEvaluator.m index d7507cce..e75cef6d 100644 --- a/XVim/XVimEqualEvaluator.m +++ b/XVim/XVimEqualEvaluator.m @@ -7,7 +7,7 @@ // #import "XVimEqualEvaluator.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "XVimMotionEvaluator.h" #import "XVimWindow.h" #import "Logger.h" @@ -18,12 +18,12 @@ @implementation XVimEqualEvaluator - (XVimEvaluator*)EQUAL{ if ([self numericArg] < 1) return nil; - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg]-1); + XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOPT_NONE, [self numericArg]-1); return [self _motionFixed:m]; } - (XVimEvaluator *)motionFixed:(XVimMotion *)motion{ - [[self sourceView] xvim_filter:motion]; + [self.currentView doFilter:motion]; return nil; } diff --git a/XVim/XVimEvaluator.h b/XVim/XVimEvaluator.h index f2699496..f5026f10 100644 --- a/XVim/XVimEvaluator.h +++ b/XVim/XVimEvaluator.h @@ -33,7 +33,7 @@ XVimMotionEvaluator */ #import "XVimRegister.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" @class XVimCommandLineEvaluator; @class XVimMotionEvaluator; @@ -47,10 +47,10 @@ XVimMotionEvaluator @interface XVimEvaluator : NSObject @property(strong) XVimWindow* window; @property(strong) XVimEvaluator* parent; -@property NSUInteger numericArg; +@property(nonatomic) NSUInteger numericArg; @property BOOL numericMode; @property(strong) NSMutableString* argumentString; -@property(strong) NSString* yankRegister; +@property(nonatomic, strong) NSString* yankRegister; @property SEL onChildCompleteHandler; - (id)initWithWindow:(XVimWindow*)window; @@ -117,9 +117,9 @@ XVimMotionEvaluator **/ - (void)didEndHandler NS_REQUIRES_SUPER; - (XVimEvaluator*)defaultNextEvaluator; -- (float)insertionPointHeightRatio; -- (float)insertionPointWidthRatio; -- (float)insertionPointAlphaRatio; +- (CGFloat)insertionPointHeightRatio; +- (CGFloat)insertionPointWidthRatio; +- (CGFloat)insertionPointAlphaRatio; - (NSString*)modeString; - (XVIM_MODE)mode; @@ -127,7 +127,7 @@ XVimMotionEvaluator - (XVimKeymap*)selectKeymapWithProvider:(id)keymapProvider; -- (NSTextView*)sourceView; +- (XVimView *)currentView; - (void)resetCompletionHandler; diff --git a/XVim/XVimEvaluator.m b/XVim/XVimEvaluator.m index 826f5b8c..4a301ba8 100644 --- a/XVim/XVimEvaluator.m +++ b/XVim/XVimEvaluator.m @@ -17,16 +17,22 @@ #import "XVimNormalEvaluator.h" #import "XVimVisualEvaluator.h" #import "XVim.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "XVimSearch.h" #import "XVimCommandLineEvaluator.h" -#import "NSString+VimHelper.h" static XVimEvaluator *_invalidEvaluator = nil; static XVimEvaluator *_noOperationEvaluator = nil; static XVimEvaluator *_popEvaluator = nil; @implementation XVimEvaluator +@synthesize window = _window; +@synthesize parent = _parent; +@synthesize numericArg = _numericArg; +@synthesize numericMode = _numericMode; +@synthesize argumentString = _argumentString; +@synthesize yankRegister = _yankRegister; +@synthesize onChildCompleteHandler = _onChildCompleteHandler; + (void)initialize { @@ -56,6 +62,7 @@ - (id)init { - (id)initWithWindow:(XVimWindow*)window{ NSAssert( nil != window, @"window must not be nil"); + DEBUG_LOG("created %@ evaluator with window %@", self.class, window); if(self = [super init]){ self.window = window; self.parent = nil; @@ -76,9 +83,9 @@ - (void)dealloc{ [super dealloc]; } -- (NSTextView*)sourceView +- (XVimView *)currentView { - return self.window.sourceView; + return self.window.currentView; } - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ @@ -87,12 +94,11 @@ - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ // Invokes each key event handler // invokes "C_k:" selector - SEL handler = [keyStroke selectorForInstance:self]; - if (handler) { + SEL handler = keyStroke.selector; + if ([self respondsToSelector:handler]) { TRACE_LOG(@"Calling SELECTOR %@", NSStringFromSelector(handler)); return [self performSelector:handler]; - } - else{ + } else { TRACE_LOG(@"SELECTOR %@ not found", NSStringFromSelector(handler)); return [self defaultNextEvaluator]; } @@ -104,15 +110,15 @@ - (XVimEvaluator*)onChildComplete:(XVimEvaluator*)childEvaluator{ } - (void)becameHandler{ - self.sourceView.xvimDelegate = self; + self.currentView.delegate = self; } - (void)cancelHandler{ - self.sourceView.xvimDelegate = nil; + self.currentView.delegate = nil; } - (void)didEndHandler{ - self.sourceView.xvimDelegate = nil; + self.currentView.delegate = nil; } - (XVimKeymap*)selectKeymapWithProvider:(id)keymapProvider { @@ -123,15 +129,15 @@ - (XVimEvaluator*)defaultNextEvaluator{ return [XVimEvaluator invalidEvaluator]; } -- (float)insertionPointHeightRatio{ +- (CGFloat)insertionPointHeightRatio{ return 1.0; } -- (float)insertionPointWidthRatio{ +- (CGFloat)insertionPointWidthRatio{ return 1.0; } -- (float)insertionPointAlphaRatio{ +- (CGFloat)insertionPointAlphaRatio{ return 0.5; } @@ -211,7 +217,10 @@ - (void)textView:(NSTextView*)view didDelete:(NSString*)deletedText withType:(TE return; } -- (XVimCommandLineEvaluator*)searchEvaluatorForward:(BOOL)forward{ +- (XVimCommandLineEvaluator*)searchEvaluatorForward:(BOOL)forward +{ + XVimView *xview = self.currentView; + return [[[XVimCommandLineEvaluator alloc] initWithWindow:self.window firstLetter:forward?@"/":@"?" history:[[XVim instance] searchHistory] @@ -224,7 +233,7 @@ - (XVimCommandLineEvaluator*)searchEvaluatorForward:(BOOL)forward{ BOOL forward = [command characterAtIndex:0] == '/'; if( command.length == 1 ){ // Repeat search - XVimMotion* m = [XVim.instance.searcher motionForRepeatSearch]; + XVimMotion *m = [XVim.instance.searcher motionForRepeatSearch]; m.motion = forward ? MOTION_SEARCH_FORWARD : MOTION_SEARCH_BACKWARD; m.count = self.numericArg; *result = m; @@ -245,9 +254,9 @@ - (XVimCommandLineEvaluator*)searchEvaluatorForward:(BOOL)forward{ BOOL forward = [command characterAtIndex:0] == '/'; XVimMotion* m = [XVim.instance.searcher motionForSearch:[command substringFromIndex:1] forward:forward]; if( [command characterAtIndex:0] == '/' ){ - [self.sourceView xvim_highlightNextSearchCandidateForward:m.regex count:self.numericArg option:m.option]; + [xview xvim_highlightNextSearchCandidateForward:m.regex count:self.numericArg option:m.option]; }else{ - [self.sourceView xvim_highlightNextSearchCandidateBackward:m.regex count:self.numericArg option:m.option]; + [xview xvim_highlightNextSearchCandidateBackward:m.regex count:self.numericArg option:m.option]; } }] autorelease]; } diff --git a/XVim/XVimExCommand.m b/XVim/XVimExCommand.m index 1b95e96f..e186c185 100644 --- a/XVim/XVimExCommand.m +++ b/XVim/XVimExCommand.m @@ -10,7 +10,7 @@ #import "XVimWindow.h" #import "XVim.h" #import "XVimSearch.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "NSString+VimHelper.h" #import "Logger.h" #import "XVimKeyStroke.h" @@ -585,35 +585,34 @@ - (void)dealloc{ // This method correnspons parsing part of get_address in ex_cmds.c - (NSUInteger)getAddress:(unichar*)parsing :(unichar**)cmdLeft inWindow:(XVimWindow*)window { - NSTextView* view = [window sourceView]; - //DVTFoldingTextStorage* storage = [view textStorage]; - //TRACE_LOG(@"Storage Class:%@", NSStringFromClass([storage class])); - NSUInteger addr = NSNotFound; - NSUInteger begin = view.selectionBegin; - NSUInteger end = view.insertionPoint; - unichar* tmp; - NSUInteger count; - unichar mark; - + XVimView *xview = window.currentView; + XVimBuffer *buffer = window.currentBuffer; + NSUInteger addr = NSNotFound; + NSUInteger begin = xview.selectionBegin; + NSUInteger end = xview.insertionPoint; + unichar *tmp; + NSUInteger count; + unichar mark; + // Parse base addr (line number) switch (*parsing) { case '.': parsing++; - addr = [view.textStorage xvim_lineNumberAtIndex:begin]; + addr = [buffer lineNumberAtIndex:begin]; break; case '$': /* '$' - last line */ parsing++; - addr = [view.textStorage xvim_numberOfLines]; + addr = [buffer numberOfLines]; break; case '\'': // XVim does support only '< '> marks for visual mode mark = parsing[1]; if( '<' == mark ){ - addr = [view.textStorage xvim_lineNumberAtIndex:begin]; + addr = [buffer lineNumberAtIndex:begin]; parsing+=2; }else if( '>' == mark ){ - addr = [view.textStorage xvim_lineNumberAtIndex:end]; + addr = [buffer lineNumberAtIndex:end]; parsing+=2; }else{ // Other marks or invalid character. XVim does not support this. @@ -731,14 +730,15 @@ - (XVimExArg*)parseCommand:(NSString*)cmd inWindow:(XVimWindow*)window // 3. parse range exarg.lineBegin = NSNotFound; exarg.lineEnd = NSNotFound; - - NSTextView* view = [window sourceView]; + + XVimView *xview = window.currentView; + XVimBuffer *buffer = window.currentBuffer; for(;;){ NSUInteger addr = [self getAddress:parsing :&parsing inWindow:window]; if( NSNotFound == addr ){ if( *parsing == '%' ){ // XVim only supports % exarg.lineBegin = 1; - exarg.lineEnd = [view.textStorage xvim_numberOfLines]; + exarg.lineEnd = buffer.numberOfLines; parsing++; } }else{ @@ -758,8 +758,8 @@ - (XVimExArg*)parseCommand:(NSString*)cmd inWindow:(XVimWindow*)window if( exarg.lineBegin == NSNotFound ){ // No range expression found. Use current line as range - exarg.lineBegin = [view.textStorage xvim_lineNumberAtIndex:view.insertionPoint]; - exarg.lineEnd = exarg.lineBegin; + exarg.lineBegin = xview.insertionLine; + exarg.lineEnd = exarg.lineBegin; } // 4. parse command @@ -814,20 +814,20 @@ - (void)executeCommand:(NSString*)cmd inWindow:(XVimWindow*)window // Actual parsing is done in following method. XVimExArg* exarg = [self parseCommand:cmd inWindow:window]; if( exarg.cmd == nil ) { - NSTextView* srcView = [window sourceView]; - NSTextStorage* storage = srcView.textStorage; - + XVimBuffer *buffer = window.currentBuffer; + XVimView *xview = window.currentView; + // Jump to location - NSUInteger pos = [storage xvim_indexOfLineNumber:exarg.lineBegin column:0]; - if( NSNotFound == pos ){ - pos = [srcView.textStorage xvim_indexOfLineNumber:[srcView.textStorage xvim_numberOfLines] column:0]; + NSUInteger pos = [buffer indexOfLineNumber:exarg.lineBegin]; + if (NSNotFound == pos) { + pos = [buffer startOfLine:buffer.length]; } - NSUInteger pos_wo_space = [srcView.textStorage xvim_nextNonblankInLineAtIndex:pos allowEOL:NO]; + NSUInteger pos_wo_space = [buffer nextNonblankInLineAtIndex:pos allowEOL:NO]; if( NSNotFound == pos_wo_space ){ pos_wo_space = pos; } - [srcView setSelectedRange:NSMakeRange(pos_wo_space,0)]; - [srcView xvim_scrollTo:[window.sourceView insertionPoint]]; + [xview.textView setSelectedRange:NSMakeRange(pos_wo_space,0)]; + [xview scrollTo:xview.insertionPoint]; return; } @@ -999,7 +999,7 @@ - (void)mapClearMode:(XVIM_MODE)mode{ } - (void)marks:(XVimExArg*)args inWindow:(XVimWindow*)window{ // This is currently impelemented for debugging purpose - NSString* local = [[XVim instance].marks dumpMarksForDocument:window.sourceView.documentURL.path]; + NSString* local = [[XVim instance].marks dumpMarksForDocument:window.currentBuffer.document.fileURL.path]; NSString* file = [[XVim instance].marks dumpFileMarks]; [[XVim instance] writeToConsole:@"----LOCAL MARKS----\n%@", local]; [[XVim instance] writeToConsole:@"----FILE MARKS----\n%@", file]; @@ -1095,8 +1095,8 @@ - (void)run:(XVimExArg*)args inWindow:(XVimWindow*)window{ - (void)set:(XVimExArg*)args inWindow:(XVimWindow*)window{ NSString* setCommand = [args.arg stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - NSTextView* srcView = [window sourceView]; XVimOptions* options = [[XVim instance] options]; + XVimView *xview = window.currentView; if( [setCommand rangeOfString:@"="].location != NSNotFound ){ // "set XXX=YYY" form @@ -1115,18 +1115,16 @@ - (void)set:(XVimExArg*)args inWindow:(XVimWindow*)window{ } if( [setCommand isEqualToString:@"wrap"] ){ - [srcView xvim_setWrapsLines:YES]; + [xview xvim_setWrapsLines:YES]; } else if( [setCommand isEqualToString:@"nowrap"] ){ - [srcView xvim_setWrapsLines:NO]; + [xview xvim_setWrapsLines:NO]; } else if( [setCommand isEqualToString:@"list!"] ){ [NSApp sendAction:@selector(toggleInvisibleCharactersShown:) to:nil from:self]; } } - (void)sort:(XVimExArg *)args inWindow:(XVimWindow *)window{ - NSTextView *view = [window sourceView]; - NSString *cmdString = [[args cmd] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; NSString *argsString = [args arg]; XVimSortOptions options = 0; @@ -1147,8 +1145,9 @@ - (void)sort:(XVimExArg *)args inWindow:(XVimWindow *)window{ options |= XVimSortOptionRemoveDuplicateLines; } } - - [view xvim_sortLinesFrom:args.lineBegin to:args.lineEnd withOptions:options]; + + XVimRange range = XVimMakeRange(args.lineBegin, args.lineEnd); + [window.currentView doSortLines:range withOptions:options]; } - (void)sub:(XVimExArg*)args inWindow:(XVimWindow*)window{ diff --git a/XVim/XVimGActionEvaluator.m b/XVim/XVimGActionEvaluator.m index be099206..51fb5708 100644 --- a/XVim/XVimGActionEvaluator.m +++ b/XVim/XVimGActionEvaluator.m @@ -8,9 +8,7 @@ #import "XVimJoinEvaluator.h" #import "XVimGActionEvaluator.h" -#import "XVimTildeEvaluator.h" -#import "XVimLowercaseEvaluator.h" -#import "XVimUppercaseEvaluator.h" +#import "XVimSwapCharsEvaluator.h" #import "XVimInsertEvaluator.h" #import "XVimKeyStroke.h" #import "XVimWindow.h" @@ -19,7 +17,7 @@ #import "XVimMarks.h" #import "XVimVisualEvaluator.h" #import "NSTextStorage+VimOperation.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" @implementation XVimGActionEvaluator @@ -41,24 +39,28 @@ - (XVimEvaluator*)f{ } - (XVimEvaluator*)i{ - XVimMark* mark = [[XVim instance].marks markForName:@"^" forDocument:self.sourceView.documentURL.path]; + XVimMark *mark = [[XVim instance].marks markForName:@"^" forDocument:self.window.currentBuffer.document]; XVimInsertionPoint mode = XVIM_INSERT_DEFAULT; + XVimBuffer *buffer = self.window.currentBuffer; + + if (mark.line != NSNotFound) { + NSUInteger newPos = [buffer indexOfLineNumber:mark.line column:mark.column]; + if (NSNotFound != newPos) { + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_POSITION, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 0); - if ( mark.line != NSNotFound) { - NSUInteger newPos = [self.sourceView.textStorage xvim_indexOfLineNumber:mark.line column:mark.column]; - if( NSNotFound != newPos ){ - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_POSITION, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 0); m.position = newPos; // set the position before the jump - XVimMark* cur_mark = [[[XVimMark alloc] init] autorelease]; - cur_mark.line = [self.sourceView insertionLine]; - cur_mark.column = [self.sourceView insertionColumn]; - cur_mark.document = [self.sourceView documentURL].path; - if( nil != mark.document){ + XVimMark *cur_mark = [[[XVimMark alloc] init] autorelease]; + XVimView *xview = self.currentView; + XVimPosition pos = xview.insertionPosition; + cur_mark.line = pos.line; + cur_mark.column = pos.column; + cur_mark.document = buffer.document.fileURL.path; + if (nil != mark.document) { [[XVim instance].marks setMark:cur_mark forName:@"'"]; } - [self.sourceView xvim_move:m]; + [xview moveCursorWithMotion:m]; mode = XVIM_INSERT_APPEND; } } @@ -67,17 +69,17 @@ - (XVimEvaluator*)i{ - (XVimEvaluator*)J{ XVimJoinEvaluator* eval = [[[XVimJoinEvaluator alloc] initWithWindow:self.window addSpace:NO] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, self.numericArg)]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, self.numericArg)]; } - (XVimEvaluator*)u{ [self.argumentString appendString:@"u"]; - return [[[XVimLowercaseEvaluator alloc] initWithWindow:self.window] autorelease]; + return [[[XVimSwapCharsEvaluator alloc] initWithWindow:self.window mode:XVIM_BUFFER_SWAP_LOWER] autorelease]; } - (XVimEvaluator*)U{ [self.argumentString appendString:@"U"]; - return [[[XVimUppercaseEvaluator alloc] initWithWindow:self.window] autorelease]; + return [[[XVimSwapCharsEvaluator alloc] initWithWindow:self.window mode:XVIM_BUFFER_SWAP_UPPER] autorelease]; } - (XVimEvaluator*)v{ @@ -85,9 +87,14 @@ - (XVimEvaluator*)v{ return [[[XVimVisualEvaluator alloc] initWithLastVisualStateWithWindow:self.window] autorelease]; } +- (XVimEvaluator*)QUESTION{ + [self.argumentString appendString:@"?"]; + return [[[XVimSwapCharsEvaluator alloc] initWithWindow:self.window mode:XVIM_BUFFER_SWAP_ROT13] autorelease]; +} + - (XVimEvaluator*)TILDE{ [self.argumentString appendString:@"~"]; - return [[[XVimTildeEvaluator alloc] initWithWindow:self.window] autorelease]; + return [[[XVimSwapCharsEvaluator alloc] initWithWindow:self.window mode:XVIM_BUFFER_SWAP_CASE] autorelease]; } @end diff --git a/XVim/XVimGMotionEvaluator.m b/XVim/XVimGMotionEvaluator.m index b50337a8..041fcf32 100644 --- a/XVim/XVimGMotionEvaluator.m +++ b/XVim/XVimGMotionEvaluator.m @@ -10,13 +10,13 @@ #import "XVimGMotionEvaluator.h" #import "XVimMotionEvaluator.h" #import "XVimKeyStroke.h" -#import "XVimMotionOption.h" #import "XVimWindow.h" #import "XVim.h" #import "XVimSearch.h" #import "Logger.h" @implementation XVimGMotionEvaluator +@synthesize motion, key; - (XVimEvaluator*)eval:(XVimKeyStroke *)keyStroke{ self.key = keyStroke; @@ -24,25 +24,25 @@ - (XVimEvaluator*)eval:(XVimKeyStroke *)keyStroke{ } - (XVimEvaluator*)g{ - self.motion = XVIM_MAKE_MOTION(MOTION_LINENUMBER, LINEWISE, MOTION_OPTION_NONE, 1); + self.motion = XVIM_MAKE_MOTION(MOTION_LINENUMBER, LINEWISE, MOPT_NONE, 1); self.motion.line = self.numericArg; return nil; } - (XVimEvaluator*)searchCurrentWord:(BOOL)forward { XVimCommandLineEvaluator* eval = [self searchEvaluatorForward:forward]; - NSRange r = [self.sourceView xvim_currentWord:MOTION_OPTION_NONE]; + NSRange r = [self.currentView xvim_currentWord:MOPT_NONE]; if( r.location == NSNotFound ){ return nil; } // This is not for matching the searching word itself // Vim also does this behavior( when matched string is not found ) - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_POSITION, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); + XVimMotion* m = XVIM_MAKE_MOTION(MOTION_POSITION, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1); m.position = r.location; - [self.sourceView xvim_move:m]; + [self.currentView moveCursorWithMotion:m]; - NSString* word = [self.sourceView.string substringWithRange:r]; + NSString* word = [self.currentView.textView.string substringWithRange:r]; NSString* searchWord = [NSRegularExpression escapedPatternForString:word]; [eval appendString:searchWord]; [eval execute]; diff --git a/XVim/XVimGVisualEvaluator.m b/XVim/XVimGVisualEvaluator.m index d3c56f62..bf1f1a86 100644 --- a/XVim/XVimGVisualEvaluator.m +++ b/XVim/XVimGVisualEvaluator.m @@ -7,9 +7,10 @@ // #import "XVimGVisualEvaluator.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "XVimWindow.h" #import "XVimJoinEvaluator.h" +#import "XVim.h" @implementation XVimGVisualEvaluator @@ -43,7 +44,7 @@ - (XVimEvaluator *)C_g{ - (XVimEvaluator*)J{ XVimJoinEvaluator* eval = [[[XVimJoinEvaluator alloc] initWithWindow:self.window addSpace:NO] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, self.numericArg)]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, self.numericArg)]; } - (XVimEvaluator *)q{ @@ -52,14 +53,18 @@ - (XVimEvaluator *)q{ } - (XVimEvaluator*)u{ - NSTextView *view = [self sourceView]; - [view xvim_makeLowerCase:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1)]; + [self.argumentString appendString:@"u"]; + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1); + [self.currentView doSwapCharacters:m mode:XVIM_BUFFER_SWAP_LOWER]; + [[XVim instance] fixOperationCommands]; return [XVimEvaluator invalidEvaluator]; } - (XVimEvaluator*)U{ - NSTextView *view = [self sourceView]; - [view xvim_makeUpperCase:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1)]; + [self.argumentString appendString:@"U"]; + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1); + [self.currentView doSwapCharacters:m mode:XVIM_BUFFER_SWAP_UPPER]; + [[XVim instance] fixOperationCommands]; return [XVimEvaluator invalidEvaluator]; } @@ -69,13 +74,18 @@ - (XVimEvaluator *)w{ } - (XVimEvaluator *)QUESTION{ - [self.window errorMessage:@"{visual}g? unimplemented" ringBell:NO]; - return [XVimEvaluator popEvaluator]; + [self.argumentString appendString:@"?"]; + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1); + [self.currentView doSwapCharacters:m mode:XVIM_BUFFER_SWAP_ROT13]; + [[XVim instance] fixOperationCommands]; + return [XVimEvaluator invalidEvaluator]; } - (XVimEvaluator*)TILDE{ - NSTextView *view = [self sourceView]; - [view xvim_swapCase:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1)]; + [self.argumentString appendString:@"~"]; + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1); + [self.currentView doSwapCharacters:m mode:XVIM_BUFFER_SWAP_CASE]; + [[XVim instance] fixOperationCommands]; return [XVimEvaluator invalidEvaluator]; } diff --git a/XVim/XVimHookManager.h b/XVim/XVimHookManager.h deleted file mode 100644 index 9e37143a..00000000 --- a/XVim/XVimHookManager.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// XVimHookManager.h -// XVim -// -// Created by Tomas Lundell on 29/04/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import - -@interface XVimHookManager : NSObject -+ (void)hookWhenPluginLoaded; -@end diff --git a/XVim/XVimHookManager.m b/XVim/XVimHookManager.m deleted file mode 100644 index 5cd7d179..00000000 --- a/XVim/XVimHookManager.m +++ /dev/null @@ -1,28 +0,0 @@ -// -// XVimHookManager.m -// XVim -// -// Created by Tomas Lundell on 29/04/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import "XVimHookManager.h" -#import "IDEEditorAreaHook.h" -#import "DVTSourceTextViewHook.h" -#import "IDESourceCodeEditorHook.h" -#import "IDEEditorHook.h" -#import "IDEWorkspaceWindowHook.h" -#import "DVTSourceTextScrollViewHook.h" - -@implementation XVimHookManager - -+ (void)hookWhenPluginLoaded{ - [IDEEditorAreaHook hook]; - [IDEWorkspaceWindowHook hook]; - [DVTSourceTextViewHook hook]; - [DVTSourceTextScrollViewHook hook]; - [IDESourceCodeEditorHook hook]; - [IDEEditorHook hook]; -} - -@end diff --git a/XVim/XVimInsertEvaluator.m b/XVim/XVimInsertEvaluator.m index b017e5c8..854371fc 100644 --- a/XVim/XVimInsertEvaluator.m +++ b/XVim/XVimInsertEvaluator.m @@ -17,7 +17,8 @@ #import "XVimMark.h" #import "XVimMarks.h" #import "XVimNormalEvaluator.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" +#import "NSTextStorage+VimOperation.h" @interface XVimInsertEvaluator() @property (nonatomic) NSRange startRange; @@ -92,25 +93,25 @@ - (XVIM_MODE)mode{ - (void)becameHandler{ [super becameHandler]; - [self.sourceView xvim_insert:_mode blockColumn:&_blockEditColumn blockLines:&_blockLines]; - self.startRange = [[self sourceView] selectedRange]; + [self.currentView doInsert:_mode blockColumn:&_blockEditColumn blockLines:&_blockLines]; + self.startRange = self.currentView.textView.selectedRange; } -- (float)insertionPointHeightRatio{ +- (CGFloat)insertionPointHeightRatio{ if(_oneCharMode){ return 0.25; } return 1.0; } -- (float)insertionPointWidthRatio{ +- (CGFloat)insertionPointWidthRatio{ if(_oneCharMode){ return 1.0; } return 0.15; } -- (float)insertionPointAlphaRatio{ +- (CGFloat)insertionPointAlphaRatio{ if(_oneCharMode){ return 0.5; } @@ -122,31 +123,30 @@ - (XVimKeymap*)selectKeymapWithProvider:(id)keymapProvider{ } - (NSString*)insertedText{ - NSTextView* view = [self sourceView]; + XVimView *xview = self.currentView; + XVimBuffer *buffer = self.window.currentBuffer; NSUInteger startLoc = self.startRange.location; - NSUInteger endLoc = [view selectedRange].location; + NSUInteger endLoc = xview.textView.selectedRange.location; NSRange textRange = NSMakeRange(NSNotFound, 0); - if( [[view string] length] == 0 ){ + if (buffer.length == 0 ){ return @""; } // If some text are deleted while editing startLoc could be out of range of the view's string. - if( ( startLoc >= [[view string] length] ) ){ - startLoc = [[view string] length] - 1; + if (startLoc >= buffer.length) { + startLoc = buffer.length - 1; } // Is this really what we want to do? // This means just moving cursor forward or backward and escape from insert mode generates the inserted test this method return. // -> The answer is 'OK'. see onMovementKeyPressed: method how it treats the inserted text. - if (endLoc > startLoc ){ + if (endLoc > startLoc) { textRange = NSMakeRange(startLoc, endLoc - startLoc); }else{ textRange = NSMakeRange(endLoc , startLoc - endLoc); } - NSString *text = [[view string] substringWithRange:textRange]; - return text; - + return [buffer.string substringWithRange:textRange]; } /* @@ -171,12 +171,15 @@ - (void)onMovementKeyPressed{ } // Store off the new start range - self.startRange = [[self sourceView] selectedRange]; + self.startRange = self.currentView.textView.selectedRange; } - (void)didEndHandler{ [super didEndHandler]; - NSTextView *sourceView = [self sourceView]; + + XVimView *xview = self.currentView; + NSTextView *sourceView = xview.textView; + XVimBuffer *buffer = self.window.currentBuffer; if( !_insertedEventsAbort && !_oneCharMode ){ NSString *text = [self insertedText]; @@ -186,40 +189,38 @@ - (void)didEndHandler{ if (_blockEditColumn != NSNotFound) { XVimRange range = XVimMakeRange(_blockLines.begin + 1, _blockLines.end); - [sourceView xvim_blockInsertFixupWithText:text mode:_mode count:self.numericArg - column:_blockEditColumn lines:range]; + [xview doInsertFixupWithText:text mode:_mode count:self.numericArg + column:_blockEditColumn lines:range]; } } // Store off any needed text XVim *xvim = [XVim instance]; - xvim.lastVisualMode = self.sourceView.selectionMode; [xvim fixOperationCommands]; - if( _oneCharMode ){ - }else if (!self.movementKeyPressed){ + if (_oneCharMode) { + } else if (!self.movementKeyPressed) { //[self recordTextIntoRegister:xvim.recordingRegister]; //[self recordTextIntoRegister:xvim.repeatRegister]; - }else if(self.lastInsertedText.length > 0){ + } else if (self.lastInsertedText.length > 0) { //[xvim.repeatRegister appendText:self.lastInsertedText]; } - [sourceView xvim_hideCompletions]; - + [xview xvim_hideCompletions]; + // Position for "^" is before escaped from insert mode - NSUInteger pos = self.sourceView.insertionPoint; - XVimMark* mark = XVimMakeMark([self.sourceView.textStorage xvim_lineNumberAtIndex:pos], [self.sourceView.textStorage xvim_columnOfIndex:pos], self.sourceView.documentURL.path); - if( nil != mark.document ){ + XVimPosition pos = xview.insertionPosition; + XVimMark *mark = XVimMakeMark(pos.line, pos.column, buffer.document); + if (nil != mark.document) { [[XVim instance].marks setMark:mark forName:@"^"]; } - - [[self sourceView] xvim_escapeFromInsert]; - + + [xview escapeFromInsertAndMoveBack:YES]; + // Position for "." is after escaped from insert mode - pos = self.sourceView.insertionPoint; - mark = XVimMakeMark([self.sourceView.textStorage xvim_lineNumberAtIndex:pos], [self.sourceView.textStorage xvim_columnOfIndex:pos], self.sourceView.documentURL.path); - if( nil != mark.document ){ + pos = xview.insertionPosition; + mark = XVimMakeMark(pos.line, pos.column, buffer.document); + if (nil != mark.document) { [[XVim instance].marks setMark:mark forName:@"."]; } - } - (BOOL)windowShouldReceive:(SEL)keySelector { @@ -229,22 +230,31 @@ - (BOOL)windowShouldReceive:(SEL)keySelector { } - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ + XVimView *xview = self.currentView; XVimEvaluator *nextEvaluator = self; - SEL keySelector = [keyStroke selectorForInstance:self]; - if (keySelector){ + + SEL keySelector = keyStroke.selector; + if ([self respondsToSelector:keySelector]) { nextEvaluator = [self performSelector:keySelector]; - }else if(self.movementKeyPressed){ - // Flag movement key as not pressed until the next movement key is pressed - self.movementKeyPressed = NO; - - // Store off the new start range - self.startRange = [[self sourceView] selectedRange]; + } else { + if(self.movementKeyPressed) { + // Flag movement key as not pressed until the next movement key is pressed + self.movementKeyPressed = NO; + + // Store off the new start range + self.startRange = self.currentView.textView.selectedRange; + } + keySelector = nil; } - + if (nextEvaluator == self && nil == keySelector){ NSEvent *event = [keyStroke toEventwithWindowNumber:0 context:nil]; if (_oneCharMode) { - if( ![self.sourceView xvim_replaceCharacters:keyStroke.character count:[self numericArg]] ){ + if (!keyStroke.isPrintable) { + [xview escapeFromInsertAndMoveBack:NO]; + nextEvaluator = [XVimEvaluator invalidEvaluator]; + } else if (![xview doReplaceCharacters:keyStroke.character count:[self numericArg]]) { + [xview escapeFromInsertAndMoveBack:NO]; nextEvaluator = [XVimEvaluator invalidEvaluator]; }else{ nextEvaluator = nil; @@ -253,10 +263,10 @@ - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ // Here we pass the key input to original text view. // The input coming to this method is already handled by "Input Method" // and the input maight be non ascii like 'あ' - if( keyStroke.modifier == 0 && isPrintable(keyStroke.character)){ - [self.sourceView insertText:keyStroke.xvimString]; + if (keyStroke.isPrintable){ + [xview.textView insertText:keyStroke.xvimString]; }else{ - [self.sourceView interpretKeyEvents:[NSArray arrayWithObject:event]]; + [xview.textView interpretKeyEvents:[NSArray arrayWithObject:event]]; } } } @@ -285,22 +295,31 @@ - (XVimEvaluator*)C_c{ return [self ESC]; } -- (void)C_yC_eHelper:(BOOL)handlingC_y { - NSUInteger currentCursorIndex = [self.sourceView selectedRange].location; - NSUInteger currentColumnIndex = [self.sourceView.textStorage xvim_columnOfIndex:currentCursorIndex]; - NSUInteger newCharIndex; +- (void)C_yC_eHelper:(BOOL)handlingC_y +{ + XVimBuffer *buffer = self.window.currentBuffer; + XVimView *xview = self.currentView; + + XVimPosition pos = xview.insertionPosition; + NSUInteger indexToCopy; + if (handlingC_y) { - newCharIndex = [self.sourceView.textStorage prevLine:currentCursorIndex column:currentColumnIndex count:[self numericArg] option:MOTION_OPTION_NONE]; + indexToCopy = [buffer indexOfLineNumber:pos.line - 1 column:pos.column]; } else { - newCharIndex = [self.sourceView.textStorage nextLine:currentCursorIndex column:currentColumnIndex count:[self numericArg] option:MOTION_OPTION_NONE]; + indexToCopy = [buffer indexOfLineNumber:pos.line + 1 column:pos.column]; } - NSUInteger newColumnIndex = [self.sourceView.textStorage xvim_columnOfIndex:newCharIndex]; - NSLog(@"Old column: %ld\tNew column: %ld", currentColumnIndex, newColumnIndex); - if (currentColumnIndex == newColumnIndex) { - unichar u = [[[self sourceView] string] characterAtIndex:newCharIndex]; - NSString *charToInsert = [NSString stringWithFormat:@"%c", u]; - [[self sourceView] insertText:charToInsert]; + if (indexToCopy == NSNotFound || [buffer isIndexAtEndOfLine:indexToCopy]) { + return; } + + unichar c = [buffer.string characterAtIndex:indexToCopy]; + NSString *s = [[NSString alloc] initWithCharacters:&c length:1]; + + NSUInteger index = xview.insertionPoint; + [buffer beginEditingAtIndex:index]; + [buffer replaceCharactersInRange:NSMakeRange(index, 0) withString:s]; + [buffer endEditingAtIndex:index + 1]; + [xview moveCursorToIndex:index + 1]; } - (XVimEvaluator*)C_y{ @@ -314,8 +333,8 @@ - (XVimEvaluator*)C_e{ } - (XVimEvaluator*)C_w{ - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_WORD_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); - [[self sourceView] xvim_delete:m andYank:NO]; + XVimMotion* m = XVIM_MAKE_MOTION(MOTION_WORD_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1); + [self.currentView doDelete:m andYank:NO]; return self; } diff --git a/XVim/XVimJoinEvaluator.m b/XVim/XVimJoinEvaluator.m index 79eb1fa5..26397eca 100644 --- a/XVim/XVimJoinEvaluator.m +++ b/XVim/XVimJoinEvaluator.m @@ -7,7 +7,7 @@ // #import "XVimJoinEvaluator.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "XVimWindow.h" @implementation XVimJoinEvaluator { @@ -27,7 +27,7 @@ - (XVimEvaluator*)motionFixed:(XVimMotion*)motion{ // J and 2J is the same motion.count--; } - [self.window.sourceView xvim_join:motion.count addSpace:_addSpace]; + [self.currentView doJoin:motion.count addSpace:_addSpace]; return nil; } diff --git a/XVim/XVimKeyStroke.h b/XVim/XVimKeyStroke.h index e6e27a31..33d1788e 100644 --- a/XVim/XVimKeyStroke.h +++ b/XVim/XVimKeyStroke.h @@ -18,7 +18,6 @@ XVimString* XVimStringFromKeyStrokes(NSArray* strokes); NSArray* XVimKeyStrokesFromXVimString(XVimString* string); NSArray* XVimKeyStrokesFromKeyNotation(NSString* notation); NSString* XVimKeyNotationFromXVimString(XVimString* string); -BOOL isPrintable(unichar c); @interface NSEvent(XVimKeyStroke) - (XVimKeyStroke*)toXVimKeyStroke; @@ -29,6 +28,7 @@ BOOL isPrintable(unichar c); @property unichar character; @property unsigned char modifier; @property (nonatomic, readonly) BOOL isNumeric; +@property (nonatomic, readonly) BOOL isPrintable; - (id)initWithCharacter:(unichar)c modifier:(unsigned char)mod; @@ -37,27 +37,12 @@ BOOL isPrintable(unichar c); // Generates an event from this key stroke - (NSEvent*)toEventwithWindowNumber:(NSInteger)num context:(NSGraphicsContext*)context; -// Creates the selector string from this key stroke -- (NSString*)toSelectorString; - // Creates a human-readable string - (NSString*)keyNotation; // Returns the selector for this object - (SEL)selector; -// Returns a selector for the target for this key stroke if one exists -- (SEL)selectorForInstance:(id)target; - -// Returns YES if the instance responds to this key stroke -- (BOOL)instanceResponds:(id)target; - -// Returns YES if the class' instances respond to this key stroke -- (BOOL)classResponds:(Class)class; - -// Returns YES if the class implements this method and does so different to its superclass -- (BOOL)classImplements:(Class)class; - // Following methods are for to be a key in NSDictionary - (NSUInteger)hash; - (BOOL)isEqual:(id)object; diff --git a/XVim/XVimKeyStroke.m b/XVim/XVimKeyStroke.m index 50a0ac0e..c037e110 100644 --- a/XVim/XVimKeyStroke.m +++ b/XVim/XVimKeyStroke.m @@ -4,21 +4,23 @@ // Copyright (c) 2012 __MyCompanyName__. All rights reserved. // +#import +#import #import "XVimKeyStroke.h" #import "NSEvent+VimHelper.h" #import "Logger.h" - +#import "XVimStringBuffer.h" /* XVimString and Key Notation - + (keymap.h file in Vim source helps understand this better) - + XVim uses internal string encoding as Vim does. - This encoding is not same as any usual character code because + This encoding is not same as any usual character code because the encoding include special flags with characters like modifiers. - + In Vim they treat all the input as a character. This is how recordings or keymapping does work. Vim uses internal code(character) to express an input(usually a key stroke). @@ -26,27 +28,27 @@ Vim uses internal code(character) to express an input(usually a key stroke). If the key is special like 'backspace' internal expression is 0x80, 'k', 'b' (3byte). Vim uses 0x80 as a escape character and followng bytes defines the key. Special keys like F1-F10 or S-F1 are all mapped to a 0x80 prefixed value. - + Vim always does following convertings. [Phisical Key Input] -> [Vim intarnal code] [Key Notation in key map] -> [Vim internal code] Key Notatation here is like or . - + And Vim interprets the [Vim intarnal code] as input and takes action. Printable codes are all stayed same. So if you record 'iabc' the internal code in Vim is 'iabc0x80kb'. You can see this when you see the recorded register with :registers - + If the key stroke has modifier Vim uses additional byte to represent the modifiers. - For example for Alt-F2 Vim internal code is like + For example for Alt-F2 Vim internal code is like 0x80,0xfc(252),0x08,0x80,k,2 where - 0x80,0xfc(252) means it has modifier flag and - 0x08 means the modifier is Alt - 0x80,k,2 is internal code for F2 - + As Vim does XVim have internal code. - XVim follows Vim way but the values we use is different. And also we use + XVim follows Vim way but the values we use is different. And also we use unichar(2bytes) instead of char in Vim This means... - Character betwee 0xF800 to 0xF8FF describes modifier flags with lower byte @@ -54,18 +56,18 @@ Vim uses internal code(character) to express an input(usually a key stroke). - For special keys like F1, arrow keys XVim does not use the same code with Vim. Cocoa defines unichar value for them so we use it instead. See or AppKit/NSEvent.h file - + So normal keys like 'a' is just 0x0061 but 'Alt+a' will be 0xF808,0x0061 (4bytes) where 0xF808 represents Alt. - + Note that all the key sequences are represented as array of unichar which is 2byte. You have to be careful about endian. So the value 'Alt+a' will be 0x08 0xF8 0x61 0x00 in byte sequence(each unichar endian is little endian) This makes easy to handle key sequence as NSString. - + Terminology: XVimString - The internal key code explained above. Notation - Key input represented by readable string like or ... - + XVimKeyStroke class: This class represents a key input. You always can convert XVimString <-> XVimKeyStroke(s). @@ -74,9 +76,9 @@ So when you handle XVimString you can convert it into XVimKeyStroke(s) and use i actuall character or modifier flag values. XVimString can represents "Sequence" of key input but XVimKeyStroke represents only one key stroke. So if XVimString has several key input it will be converted into array of XVimKeyStorke. - + In other words you can serialize/deserialize XVimKeyStroke(s) with XVimString - + Modifier Flags: Following bit mask for modifiers is from NSEvent.h We do not use this mask because modifier mask must be fits in 1 byte length. @@ -119,270 +121,284 @@ So when you handle XVimString you can convert it into XVimKeyStroke(s) and use i // If multiple key expressions are mapped to one char code // Put default key expression at the end of the same keys. // The last one will be used when converting charcode -> key expression. - {@"NUL", 0, @"NUL"}, - {@"SOH", 1, @"SOH"}, - {@"STX", 2, @"STX"}, - {@"ETX", 3, @"ETX" }, - {@"EOT", 4, @"EOT"}, - {@"ENQ", 5, @"ENQ"}, - {@"ACK", 6, @"ACK"}, - {@"BEL", 7, @"BEL"}, - {@"BS", 8, @"BS" }, - {@"HT", 9, @"TAB"}, - {@"TAB", 9, @"TAB"}, // Default notation - {@"NL", 10, @"NL"}, - {@"VT", 11, @"VT"}, - {@"NP", 12, @"NP"}, - {@"RETURN", 13, @"CR"}, - {@"ENTER", 13, @"CR"}, - {@"CR", 13, @"CR"}, // Default notation - {@"SO", 14, @"SO"}, - {@"SI", 15, @"SI"}, - {@"DLE", 16, @"DLE"}, - {@"DC1", 17, @"DC1"}, - {@"DC2", 18, @"DC2"}, - {@"DC3", 19, @"DC3"}, - {@"DC4", 20, @"DC4"}, - {@"NAK", 21, @"NAK"}, - {@"SYN", 22, @"SYN"}, - {@"ETB", 23, @"ETB"}, - {@"CAN", 24, @"CAN"}, - {@"EM", 25, @"EM"}, - {@"SUB", 26, @"SUB"}, - {@"ESC", 27, @"ESC"}, - {@"FS", 28, @"FS"}, - {@"GS", 29, @"GS"}, - {@"RS", 30, @"RS"}, - {@"US", 31, @"US"}, - {@"SPACE", 32, @"SPACE"}, - {@" ", 32, @"SPACE"}, // Default notation - {@"!", 33, @"EXCLAMATION"}, - {@"\"", 34, @"DQUOTE"}, - {@"#", 35, @"NUMBER"}, - {@"$", 36, @"DOLLAR"}, - {@"%", 37, @"PERCENT"}, - {@"&", 38, @"AMPASAND"}, - {@"'", 39, @"SQUOTE"}, - {@"(", 40, @"LPARENTHESIS"}, - {@")", 41, @"RPARENTHESIS"}, - {@"*", 42, @"ASTERISK"}, - {@"+", 43, @"PLUS"}, - {@",", 44, @"COMMA"}, - {@"-", 45, @"MINUS"}, - {@".", 46, @"DOT"}, - {@"/", 47, @"SLASH"}, - {@"0", 48, @"NUM0"}, - {@"1", 49, @"NUM1"}, - {@"2", 50, @"NUM2"}, - {@"3", 51, @"NUM3"}, - {@"4", 52, @"NUM4"}, - {@"5", 53, @"NUM5"}, - {@"6", 54, @"NUM6"}, - {@"7", 55, @"NUM7"}, - {@"8", 56, @"NUM8"}, - {@"9", 57, @"NUM9"}, - {@":", 58, @"COLON"}, - {@";", 59, @"SEMICOLON"}, - {@"LT",60, @"LESSTHAN"}, - {@"<", 60, @"LESSTHAN"}, // Default notation - {@"=", 61, @"EQUAL"}, - {@">", 62, @"GREATERTHAN"}, - {@"?", 63, @"QUESTION"}, - {@"@", 64, @"AT"}, - {@"A", 65, @"A" }, - {@"B", 66, @"B"}, - {@"C", 67, @"C"}, - {@"D", 68, @"D"}, - {@"E", 69, @"E"}, - {@"F", 70, @"F"}, - {@"G", 71, @"G"}, - {@"H", 72, @"H"}, - {@"I", 73, @"I"}, - {@"J", 74, @"J"}, - {@"K", 75, @"K"}, - {@"L", 76, @"L"}, - {@"M", 77, @"M"}, - {@"N", 78, @"N"}, - {@"O", 79, @"O"}, - {@"P", 80, @"P"}, - {@"Q", 81, @"Q"}, - {@"R", 82, @"R"}, - {@"S", 83, @"S"}, - {@"T", 84, @"T"}, - {@"U", 85, @"U"}, - {@"V", 86, @"V"}, - {@"W", 87, @"W"}, - {@"X", 88, @"X"}, - {@"Y", 89, @"Y"}, - {@"Z", 90, @"Z"}, - {@"[", 91, @"LSQUAREBRACKET"}, - {@"BSLASH", 92, @"BACKSLASH"}, - {@"\\",92, @"BACKSLASH"}, // Default noattion - {@"]",93, @"RSQUAREBRACKET"}, - {@"^",94, @"CARET"}, - {@"_",95, @"UNDERSCORE"}, - {@"`",96, @"BACKQUOTE"}, - {@"a",97, @"a"}, - {@"b",98, @"b"}, - {@"c",99, @"c"}, - {@"d",100, @"d"}, - {@"e",101, @"e"}, - {@"f",102, @"f"}, - {@"g",103, @"g"}, - {@"h",104, @"h"}, - {@"i",105, @"i"}, - {@"j",106, @"j"}, - {@"k",107, @"k"}, - {@"l",108, @"l"}, - {@"m",109, @"m"}, - {@"n",110, @"n"}, - {@"o",111, @"o"}, - {@"p",112, @"p"}, - {@"q",113, @"q"}, - {@"r",114, @"r"}, - {@"s",115, @"s"}, - {@"t",116, @"t"}, - {@"u",117, @"u"}, - {@"v",118, @"v"}, - {@"w",119, @"w"}, - {@"x",120, @"x"}, - {@"y",121, @"y"}, - {@"z",122, @"z"}, - {@"{",123, @"LBRACE"}, - {@"BAR",124, @"BAR"}, - {@"|",124, @"BAR"}, // Default notation - {@"}",125, @"RBRACE"}, - {@"~",126, @"TILDE"}, - {@"BS",127, @"BS"}, - {@"UP",63232, @"Up"}, - {@"DOWN", 63233, @"Down"}, - {@"LEFT", 63234, @"Left"}, - {@"RIGHT", 63235, @"Right"}, - {@"F1", 63236, @"F1"}, - {@"F2", 63237, @"F2"}, - {@"F3", 63238, @"F3"}, - {@"F4", 63239, @"F4"}, - {@"F5", 63240, @"F5"}, - {@"F6", 63241, @"F6"}, - {@"F7", 63242, @"F7"}, - {@"F8", 63243, @"F8"}, - {@"F9", 63244, @"F9"}, - {@"F10", 63245, @"F10"}, - {@"F11", 63246, @"F11"}, - {@"F12", 63247, @"F12"}, - {@"DEL", 63272, @"DEL"}, - {@"HOME", 63273, @"Home"}, - {@"END", 63275, @"End"}, - {@"PAGEUP", 63276, @"Pageup"}, - {@"PAGEDOWN", 63277, @"Pagedown"} + { @"NUL", 0, @"NUL"}, + { @"SOH", 1, @"SOH"}, + { @"STX", 2, @"STX"}, + { @"ETX", 3, @"ETX" }, + { @"EOT", 4, @"EOT"}, + { @"ENQ", 5, @"ENQ"}, + { @"ACK", 6, @"ACK"}, + { @"BEL", 7, @"BEL"}, + { @"BS", 8, @"BS" }, + { @"HT", 9, @"TAB"}, + { @"TAB", 9, @"TAB"}, // Default notation + { @"NL", 10, @"NL"}, + { @"VT", 11, @"VT"}, + { @"NP", 12, @"NP"}, + { @"RETURN", 13, @"CR"}, + { @"ENTER", 13, @"CR"}, + { @"CR", 13, @"CR"}, // Default notation + { @"SO", 14, @"SO"}, + { @"SI", 15, @"SI"}, + { @"DLE", 16, @"DLE"}, + { @"DC1", 17, @"DC1"}, + { @"DC2", 18, @"DC2"}, + { @"DC3", 19, @"DC3"}, + { @"DC4", 20, @"DC4"}, + { @"NAK", 21, @"NAK"}, + { @"SYN", 22, @"SYN"}, + { @"ETB", 23, @"ETB"}, + { @"CAN", 24, @"CAN"}, + { @"EM", 25, @"EM"}, + { @"SUB", 26, @"SUB"}, + { @"ESC", 27, @"ESC"}, + { @"FS", 28, @"FS"}, + { @"GS", 29, @"GS"}, + { @"RS", 30, @"RS"}, + { @"US", 31, @"US"}, + { @"SPACE", 32, @"SPACE"}, + { @" ", 32, @"SPACE"}, // Default notation + { @"!", 33, @"EXCLAMATION"}, + { @"\"", 34, @"DQUOTE"}, + { @"#", 35, @"NUMBER"}, + { @"$", 36, @"DOLLAR"}, + { @"%", 37, @"PERCENT"}, + { @"&", 38, @"AMPASAND"}, + { @"'", 39, @"SQUOTE"}, + { @"(", 40, @"LPARENTHESIS"}, + { @")", 41, @"RPARENTHESIS"}, + { @"*", 42, @"ASTERISK"}, + { @"+", 43, @"PLUS"}, + { @",", 44, @"COMMA"}, + { @"-", 45, @"MINUS"}, + { @".", 46, @"DOT"}, + { @"/", 47, @"SLASH"}, + { @"0", 48, @"NUM0"}, + { @"1", 49, @"NUM1"}, + { @"2", 50, @"NUM2"}, + { @"3", 51, @"NUM3"}, + { @"4", 52, @"NUM4"}, + { @"5", 53, @"NUM5"}, + { @"6", 54, @"NUM6"}, + { @"7", 55, @"NUM7"}, + { @"8", 56, @"NUM8"}, + { @"9", 57, @"NUM9"}, + { @":", 58, @"COLON"}, + { @";", 59, @"SEMICOLON"}, + { @"LT", 60, @"LESSTHAN"}, + { @"<", 60, @"LESSTHAN"}, // Default notation + { @"=", 61, @"EQUAL"}, + { @">", 62, @"GREATERTHAN"}, + { @"?", 63, @"QUESTION"}, + { @"@", 64, @"AT"}, + { @"[", 91, @"LSQUAREBRACKET"}, + { @"BSLASH", 92, @"BACKSLASH"}, + { @"\\", 92, @"BACKSLASH"}, // Default noattion + { @"]", 93, @"RSQUAREBRACKET"}, + { @"^", 94, @"CARET"}, + { @"_", 95, @"UNDERSCORE"}, + { @"`", 96, @"BACKQUOTE"}, + { @"{", 123, @"LBRACE"}, + { @"BAR", 124, @"BAR"}, + { @"|", 124, @"BAR"}, // Default notation + { @"}", 125, @"RBRACE"}, + { @"~", 126, @"TILDE"}, + { @"BS", 127, @"BS"}, + + { @"UP", NSUpArrowFunctionKey, @"Up" }, + { @"DOWN", NSDownArrowFunctionKey, @"Down" }, + { @"LEFT", NSLeftArrowFunctionKey, @"Left" }, + { @"RIGHT", NSRightArrowFunctionKey, @"Right" }, + { @"F1", NSF1FunctionKey, @"F1" }, + { @"F2", NSF2FunctionKey, @"F2" }, + { @"F3", NSF3FunctionKey, @"F3" }, + { @"F4", NSF4FunctionKey, @"F4" }, + { @"F5", NSF5FunctionKey, @"F5" }, + { @"F6", NSF6FunctionKey, @"F6" }, + { @"F7", NSF7FunctionKey, @"F7" }, + { @"F8", NSF8FunctionKey, @"F8" }, + { @"F9", NSF9FunctionKey, @"F9" }, + { @"F10", NSF10FunctionKey, @"F10" }, + { @"F11", NSF11FunctionKey, @"F11" }, + { @"F12", NSF12FunctionKey, @"F12" }, + { @"F13", NSF13FunctionKey, @"F13" }, + { @"F14", NSF14FunctionKey, @"F14" }, + { @"F15", NSF15FunctionKey, @"F15" }, + { @"F16", NSF16FunctionKey, @"F16" }, + { @"F17", NSF17FunctionKey, @"F17" }, + { @"F18", NSF18FunctionKey, @"F18" }, + { @"F19", NSF19FunctionKey, @"F19" }, + { @"F20", NSF20FunctionKey, @"F20" }, + { @"F21", NSF21FunctionKey, @"F21" }, + { @"F22", NSF22FunctionKey, @"F22" }, + { @"F23", NSF23FunctionKey, @"F23" }, + { @"F24", NSF24FunctionKey, @"F24" }, + { @"F25", NSF25FunctionKey, @"F25" }, + { @"F26", NSF26FunctionKey, @"F26" }, + { @"F27", NSF27FunctionKey, @"F27" }, + { @"F28", NSF28FunctionKey, @"F28" }, + { @"F29", NSF29FunctionKey, @"F29" }, + { @"F30", NSF30FunctionKey, @"F30" }, + { @"F31", NSF31FunctionKey, @"F31" }, + { @"F32", NSF32FunctionKey, @"F32" }, + { @"F33", NSF33FunctionKey, @"F33" }, + { @"F34", NSF34FunctionKey, @"F34" }, + { @"F35", NSF35FunctionKey, @"F35" }, + { @"INS", NSInsertFunctionKey, @"Insert" }, + + { @"DEL", NSDeleteFunctionKey, @"DEL" }, + { @"HOME", NSHomeFunctionKey, @"Home" }, + { @"BEGIN", NSBeginFunctionKey, @"Begin" }, + { @"END", NSEndFunctionKey, @"End" }, + { @"PGUP", NSPageUpFunctionKey, @"Pageup" }, + { @"PGDN", NSPageDownFunctionKey, @"Pagedown" }, + { @"PRINTSCREEN", NSPrintScreenFunctionKey, @"PrintScreen" }, + { @"SCREENLOCK", NSScrollLockFunctionKey, @"ScrLock" }, + { @"PAUSE", NSPauseFunctionKey, @"Pause" }, + { @"SYSREQ", NSSysReqFunctionKey, @"SysReq" }, + { @"BREAK", NSBreakFunctionKey, @"Break" }, + { @"RESET", NSResetFunctionKey, @"Reset" }, + { @"STOP", NSStopFunctionKey, @"Stop" }, + { @"MENU", NSMenuFunctionKey, @"Menu" }, + { @"USER", NSUserFunctionKey, @"User" }, + { @"SYSTEM", NSSystemFunctionKey, @"System" }, + { @"PRINT", NSPrintFunctionKey, @"Print" }, + { @"CLEARLINE", NSClearLineFunctionKey, @"ClearLine" }, + { @"CLEARDISPLAY", NSClearDisplayFunctionKey, @"ClearDisplay" }, + { @"INSLINE", NSInsertLineFunctionKey, @"InsLine" }, + { @"DELLINE", NSDeleteLineFunctionKey, @"DelLine" }, + { @"INSCHAR", NSInsertCharFunctionKey, @"InsChar" }, + { @"DELCHAR", NSDeleteCharFunctionKey, @"DelChar" }, + { @"PREV", NSPrevFunctionKey, @"Prev" }, + { @"NEXT", NSNextFunctionKey, @"Next" }, + { @"SELECT", NSSelectFunctionKey, @"Select" }, + { @"EXECUTE", NSExecuteFunctionKey, @"Execute" }, + { @"UNDO", NSUndoFunctionKey, @"Undo" }, + { @"REDO", NSRedoFunctionKey, @"Redo" }, + { @"FIND", NSFindFunctionKey, @"Find" }, + { @"HELP", NSHelpFunctionKey, @"Help" }, + { @"MODESWITCH", NSModeSwitchFunctionKey, @"ModeSwitch" }, + + { nil, 0, nil }, }; static NSMutableDictionary *s_unicharToSelector = nil; static NSMutableDictionary *s_keyToUnichar = nil; static NSMutableDictionary *s_unicharToKey= nil; +static locale_t s_locale; -static void initUnicharToSelector(){ - if( nil == s_unicharToSelector ){ +NS_INLINE void init_maps(void) +{ + static dispatch_once_t once; + dispatch_once(&once, ^{ s_unicharToSelector = [[NSMutableDictionary alloc] init]; // Never release - for( unsigned int i = 0; i < sizeof(key_maps)/sizeof(struct key_map); i++ ){ - [s_unicharToSelector setObject:key_maps[i].selector forKey:[NSNumber numberWithUnsignedInteger:key_maps[i].c]]; + s_keyToUnichar = [[NSMutableDictionary alloc] init]; // Never release + s_unicharToKey= [[NSMutableDictionary alloc] init]; // Never release + + for (NSUInteger i = 0; key_maps[i].key; i++) { + NSNumber *c = @(key_maps[i].c); + NSString *key = key_maps[i].key; + NSString *sel = key_maps[i].selector; + + [s_unicharToSelector setObject:sel forKey:c]; + [s_keyToUnichar setObject:c forKey:key]; + [s_unicharToKey setObject:key forKey:c]; + // any UTF-8 works because we ask for iswprint() or wcwidth() + s_locale = newlocale(LC_CTYPE_MASK, "en_US.UTF-8", NULL); } - } + }); } -static void initKeyToUnichar(){ - if( nil == s_keyToUnichar){ - s_keyToUnichar = [[NSMutableDictionary alloc] init]; // Never release - for( unsigned int i = 0; i < sizeof(key_maps)/sizeof(struct key_map); i++ ){ - [s_keyToUnichar setObject:[NSNumber numberWithUnsignedInteger:key_maps[i].c] forKey:key_maps[i].key]; - } - } +NS_INLINE BOOL isNSFunctionKey(unichar c) +{ + // see NSEvent.h: OpenStep reserves the range 0xF700-0xF8FF for Function Keys + return 0xF700 <= c && c < 0xF900; } -static void initUnicharToKey(){ - if( nil == s_unicharToKey){ - s_unicharToKey= [[NSMutableDictionary alloc] init]; // Never release - for( unsigned int i = 0; i < sizeof(key_maps)/sizeof(struct key_map); i++ ){ - [s_unicharToKey setObject:key_maps[i].key forKey:[NSNumber numberWithUnsignedInteger:key_maps[i].c]]; - } - } +NS_INLINE BOOL isPrintable(unichar c) +{ + init_maps(); + + return !isNSFunctionKey(c) && iswprint_l(c, s_locale); } -static BOOL isValidKey(NSString* key){ - if( nil == s_keyToUnichar ){ - initKeyToUnichar(); +NS_INLINE BOOL isValidKey(NSString *key) +{ + init_maps(); + + if (key.length == 0) { + return NO; } - // Notations like , are all case insensitive - if( [key length] > 1 ){ - key = [key uppercaseString]; + if (key.length == 1) { + return isPrintable([key characterAtIndex:0]); } - return nil != [s_keyToUnichar objectForKey:key]; -} -static NSString* selectorFromUnichar(unichar c){ - if( nil == s_unicharToSelector ){ - initUnicharToSelector(); - } - return [s_unicharToSelector objectForKey:[NSNumber numberWithUnsignedInteger:c]]; + return [s_keyToUnichar objectForKey:key.uppercaseString] != 0; } -static unichar unicharFromKey(NSString* key){ - if( nil == s_keyToUnichar ){ - initKeyToUnichar(); - } - - if( !isValidKey(key) ){ +NS_INLINE unichar unicharFromKey(NSString *key) +{ + init_maps(); + + if (key.length == 0) { return (unichar)-1; - }else{ - if( [key length] > 1){ - key = [key uppercaseString]; - } - return [[s_keyToUnichar objectForKey:key] unsignedIntegerValue]; } -} + if (key.length == 1) { + unichar c = [key characterAtIndex:0]; -static NSString* keyFromUnichar(unichar c){ - if( nil == s_unicharToKey){ - initUnicharToKey(); + return isPrintable(c) ? c : (unichar)-1; } - return [s_unicharToKey objectForKey:[NSNumber numberWithUnsignedInteger:c]]; + + return [[s_keyToUnichar objectForKey:key.uppercaseString] unsignedIntegerValue]; } -BOOL isPrintable(unichar c){ - // FIXME: - // There may be better difinition of printable characters in unicode - if( c < 32 || c == 127 || ( 63232 <= c && c <= 63277 ) ){ - return NO; +NS_INLINE NSString *keyFromUnichar(unichar c) +{ + init_maps(); + + NSString *key = [s_unicharToKey objectForKey:@(c)]; + if (key) { + return key; } - return YES; + if (isPrintable(c)) { + return [NSString stringWithCharacters:&c length:1]; + } + return @"?"; } -static BOOL isModifier(unichar c){ - return ( XVIM_MODIFIER_MIN <= c && c <= XVIM_MODIFIER_MAX ); +NS_INLINE BOOL isModifier(unichar c) +{ + return (XVIM_MODIFIER_MIN <= c && c <= XVIM_MODIFIER_MAX); } -static XVimString* MakeXVimString( unichar character, unsigned short modifier){ - NSMutableString* str = [[[NSMutableString alloc] init] autorelease]; +static XVimString *MakeXVimString(unichar character, unsigned short modifier) +{ + NSMutableString *str = [[[NSMutableString alloc] init] autorelease]; + + init_maps(); + // If the character is pritable we do not consider Shift modifier // For example and ! is same - if( isPrintable(character) ){ + if (isPrintable(character)) { modifier = modifier & ~XVIM_MOD_SHIFT; } - if( modifier != 0 ){ + if (modifier != 0) { [str appendFormat:@"%C", XVIM_MAKE_MODIFIER(modifier)]; } [str appendFormat:@"%C", character]; return str; } -static XVimString* XVimStringFromKeyNotationImpl(NSString* string, NSUInteger* index){ - NSUInteger starti = *index; - - NSUInteger modifierFlags = 0; +static XVimString *XVimStringFromKeyNotationImpl(NSString *string, NSUInteger *index) +{ + NSUInteger starti = *index; + + NSUInteger modifierFlags = 0; NSUInteger p = starti; NSUInteger length = [string length]; - if ([string characterAtIndex:starti] == '<') { - // Find modifier flags, if any + + if ([string characterAtIndex:starti] == '<') { + // Find modifier flags, if any p += 1; // skip first '<' letter NSRange keyEnd = [string rangeOfString:@">" options:0 range:NSMakeRange(p, length-p)]; if( keyEnd.location != NSNotFound ){ @@ -410,28 +426,28 @@ static BOOL isModifier(unichar c){ } p+=2; } - - NSString* key = [string substringWithRange:NSMakeRange(p, keyEnd.location-p)]; - if( isValidKey(key) ){ - if( 0 == modifierFlags ){ + + NSString *key = [string substringWithRange:NSMakeRange(p, keyEnd.location-p)]; + if (isValidKey(key)) { + if (0 == modifierFlags) { //If it does not have modifier flag the key must be multiple letters - if( [key length] > 1 ){ + if ([key length] > 1) { *index = keyEnd.location+1; unichar c = unicharFromKey(key); - return MakeXVimString( c, modifierFlags); + return MakeXVimString(c, modifierFlags); } - }else{ + } else { //This is modifier flag + valid key *index = keyEnd.location+1; unichar c = unicharFromKey(key); - return MakeXVimString( c, modifierFlags); + return MakeXVimString(c, modifierFlags); } } } // if it not valid key like "" or "" take first letter "<" as a key // Just go through. } - + // Simple one letter key NSString* key = [string substringWithRange:NSMakeRange(starti, 1)]; unichar c = unicharFromKey(key); @@ -440,16 +456,16 @@ static BOOL isModifier(unichar c){ } XVimString* XVimStringFromKeyNotation(NSString* notation){ - NSUInteger index = 0; - NSUInteger len = notation.length; + NSUInteger index = 0; + NSUInteger len = notation.length; NSMutableString* str = [[[NSMutableString alloc] init] autorelease]; - while (index < len){ + while (index < len){ XVimString* oneKey = XVimStringFromKeyNotationImpl(notation, &index); - if( oneKey == nil ){ + if( oneKey == nil ){ break; } [str appendString:oneKey]; - } + } return str; } @@ -473,7 +489,7 @@ static BOOL isModifier(unichar c){ c2 = c1; c1 = 0; } - + XVimKeyStroke* stroke = [[[XVimKeyStroke alloc] initWithCharacter:c2 modifier:c1] autorelease]; [array addObject:stroke]; } @@ -500,11 +516,15 @@ - (XVimKeyStroke*)toXVimKeyStroke{ return nil; } unichar c = [[self charactersIgnoringModifiers] characterAtIndex:0]; - // We unset NSFunctionKeyMask bit for function keys (7F00 and above) NSUInteger mod = self.modifierFlags; - if( c >= 0x7F00 ){ + if (isNSFunctionKey(c)) { + // We unset NSFunctionKeyMask bit for function keys (7F00 and above) mod &= (NSUInteger)~NSFunctionKeyMask; } + if (c == 0x19 && (mod & NSDeviceIndependentModifierFlagsMask) == NSShiftKeyMask) { + // S-EM really is S-Tab + c = '\t'; + } mod = NSMOD2XVIMMOD(mod); return [[[XVimKeyStroke alloc] initWithCharacter:c modifier:(unsigned char)mod] autorelease]; } @@ -517,51 +537,53 @@ - (XVimString*)toXVimString{ @implementation XVimKeyStroke +@synthesize character = _character, modifier = _modifier; + ++ (void)initialize +{ + init_maps(); +} - (id)initWithCharacter:(unichar)c modifier:(unsigned char)mod{ if( self = [super init] ){ - self.character = c; - self.modifier = mod; + _character = c; + _modifier = mod; } return self; } - (XVimString*)xvimString{ - return MakeXVimString(self.character, self.modifier); + return MakeXVimString(_character, _modifier); } - (BOOL) isNumeric{ - if( self.modifier == 0 && ( '0' <= self.character && self.character <= '9' ) ){ - return YES; - }else{ - return NO; - } + return _modifier == 0 && ('0' <= _character && _character <= '9'); } - (NSUInteger)hash{ - return self.modifier + self.character; + return _modifier + _character; } - (BOOL)isEqual:(id)object{ - if (object == self) { - return YES; - } - if (!object || ![object isKindOfClass:[self class]]){ - return NO; - } - XVimKeyStroke* other = object; - return self.character == other.character && self.modifier== other.modifier; + if (object == self) { + return YES; + } + if (!object || ![object isKindOfClass:[self class]]){ + return NO; + } + XVimKeyStroke* other = object; + return _character == other.character && _modifier== other.modifier; } - (id)copyWithZone:(NSZone *)zone { - return [[XVimKeyStroke allocWithZone:zone] initWithCharacter:self.character modifier:self.modifier]; + return [[XVimKeyStroke allocWithZone:zone] initWithCharacter:_character modifier:_modifier]; } - (NSEvent*)toEventwithWindowNumber:(NSInteger)num context:(NSGraphicsContext*)context; { - unichar c = self.character; + unichar c = _character; NSString *characters = [NSString stringWithCharacters:&c length:1]; - NSUInteger mflags = XVIMMOD2NSMOD(self.modifier); - + NSUInteger mflags = XVIMMOD2NSMOD(_modifier); + return [NSEvent keyEventWithType:NSKeyDown location:NSMakePoint(0, 0) modifierFlags:mflags @@ -570,18 +592,18 @@ - (NSEvent*)toEventwithWindowNumber:(NSInteger)num context:(NSGraphicsContext*)c context:context characters:characters charactersIgnoringModifiers:characters - isARepeat:NO + isARepeat:NO keyCode:0]; } - (NSString*)description{ NSMutableString *str = [[NSMutableString alloc] init]; - if (0 != self.modifier) { - [str appendFormat:@"mod{0x%02x 0x%02x} ", KS_MODIFIER, self.modifier]; + if (0 != _modifier) { + [str appendFormat:@"mod{0x%02x 0x%02x} ", KS_MODIFIER, _modifier]; } - unichar c = self.character; + unichar c = _character; if (isPrintable(c)) { [str appendFormat:@"code{%C} ", c]; }else{ @@ -592,92 +614,77 @@ - (NSString*)description{ return [str autorelease]; } +- (BOOL)isPrintable +{ + return !_modifier && isPrintable(_character); +} + - (NSString*)keyNotation{ - NSMutableString *keyStr = [[NSMutableString alloc] init]; - unichar charcode = self.character; + NSMutableString *keyStr = [[NSMutableString alloc] init]; + unichar charcode = _character; - if (self.modifier || !isPrintable(charcode)) { + if (_modifier || !isPrintable(charcode)) { [keyStr appendString:@"<"]; } - - if (self.modifier & XVIM_MOD_SHIFT) { - [keyStr appendString:@"S-"]; - } - if (self.modifier & XVIM_MOD_CTRL) { - [keyStr appendString:@"C-"]; - } - if (self.modifier & XVIM_MOD_ALT) { - [keyStr appendString:@"M-"]; - } - if (self.modifier & XVIM_MOD_CMD) { - [keyStr appendString:@"D-"]; - } - if (self.modifier & XVIM_MOD_FUNC) { - [keyStr appendString:@"F-"]; - } + + if (_modifier & XVIM_MOD_SHIFT) { + [keyStr appendString:@"S-"]; + } + if (_modifier & XVIM_MOD_CTRL) { + [keyStr appendString:@"C-"]; + } + if (_modifier & XVIM_MOD_ALT) { + [keyStr appendString:@"M-"]; + } + if (_modifier & XVIM_MOD_CMD) { + [keyStr appendString:@"D-"]; + } + if (_modifier & XVIM_MOD_FUNC) { + [keyStr appendString:@"F-"]; + } [keyStr appendString:keyFromUnichar(charcode)]; - - if (self.modifier || !isPrintable(charcode)) { + + if (_modifier || !isPrintable(charcode)) { [keyStr appendString:@">"]; } return [keyStr autorelease]; } -- (NSString*) toSelectorString { - // S- Shift - // C- Control - // M- Option - // D- Command - // F_ Function (not F1,F2.. but 'Function' key) - NSMutableString* keyStr = [[[NSMutableString alloc] init] autorelease]; - if( self.modifier & XVIM_MOD_SHIFT){ - [keyStr appendString:@"S_"]; - } - if( self.modifier & XVIM_MOD_CTRL){ - [keyStr appendString:@"C_"]; - } - if( self.modifier & XVIM_MOD_ALT){ - [keyStr appendString:@"M_"]; - } - if( self.modifier & XVIM_MOD_CMD){ - [keyStr appendString:@"D_"]; - } - if( self.modifier & XVIM_MOD_FUNC){ - [keyStr appendString:@"F_"]; - } - - NSString *keyname = selectorFromUnichar(self.character); - if (keyname) { - [keyStr appendString:keyname]; - } - - return keyStr; -} - -- (SEL)selector { - return NSSelectorFromString([self toSelectorString]); -} +- (SEL)selector +{ + char buf[128]; + int pos = 0; -- (SEL)selectorForInstance:(id)target { - if( [target respondsToSelector:self.selector] ){ - return self.selector; - }else{ - return nil; + if (_modifier & XVIM_MOD_SHIFT) { + buf[pos++] = 'S'; buf[pos++] = '_'; + } + if (_modifier & XVIM_MOD_CTRL) { + buf[pos++] = 'C'; buf[pos++] = '_'; + } + if (_modifier & XVIM_MOD_ALT) { + buf[pos++] = 'M'; buf[pos++] = '_'; + } + if (_modifier & XVIM_MOD_CMD) { + buf[pos++] = 'D'; buf[pos++] = '_'; + } + if (_modifier & XVIM_MOD_FUNC) { + buf[pos++] = 'F'; buf[pos++] = '_'; } -} -- (BOOL)instanceResponds:(id)target { - return [self selectorForInstance:target] != nil; -} + if ((_character >= 'a' && _character <= 'z') || (_character >= 'A' && _character <= 'Z')) { + buf[pos++] = _character; + buf[pos++] = '\0'; + } else { + NSString *keyname = [s_unicharToSelector objectForKey:@(_character)]; -- (BOOL)classResponds:(Class)class{ - return [class instancesRespondToSelector:self.selector]; -} + if (!keyname) { + return @selector(__invalid_selector_name__); + } + strcpy(buf + pos, keyname.UTF8String); + } -- (BOOL)classImplements:(Class)class { - IMP imp = [class instanceMethodForSelector:self.selector]; - return imp && imp != [[class superclass] instanceMethodForSelector:self.selector]; + return sel_getUid(buf); } -@end \ No newline at end of file +@end diff --git a/XVim/XVimKeymap.m b/XVim/XVimKeymap.m index 4e686e2f..1e1b4e49 100644 --- a/XVim/XVimKeymap.m +++ b/XVim/XVimKeymap.m @@ -22,6 +22,10 @@ - (BOOL)hasChild; @end @implementation XVimKeymapNode +@synthesize dict; +@synthesize remap; +@synthesize target; + - (id)init{ if (self = [super init]){ self.dict = [[[NSMutableDictionary alloc] init] autorelease]; @@ -45,6 +49,11 @@ - (BOOL)hasChild{ @implementation XVimKeymapContext +@synthesize inputKeys; +@synthesize lastMappedKeys; +@synthesize lastMappedNode; +@synthesize node; + - (id)init { if (self = [super init]){ self.inputKeys = [[[NSMutableString alloc] init] autorelease]; @@ -84,6 +93,8 @@ @interface XVimKeymap() @end @implementation XVimKeymap +@synthesize root; + - (id)init{ self = [super init]; if (self) { diff --git a/XVim/XVimLowercaseEvaluator.m b/XVim/XVimLowercaseEvaluator.m deleted file mode 100644 index b800a12f..00000000 --- a/XVim/XVimLowercaseEvaluator.m +++ /dev/null @@ -1,30 +0,0 @@ -// -// XVimLowercaseEvaluator.m -// XVim -// -// Created by Tomas Lundell on 6/04/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import "XVimLowercaseEvaluator.h" -#import "XVimWindow.h" -#import "XVim.h" - -@implementation XVimLowercaseEvaluator - -- (XVimEvaluator*)u{ - if ([self numericArg] < 1) - return nil; - - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg]-1); - return [self _motionFixed:m]; -} - --(XVimEvaluator*)motionFixed:(XVimMotion*)motion{ - [[self sourceView] xvim_makeLowerCase:motion]; - return nil; -} - - -@end - diff --git a/XVim/XVimMark.h b/XVim/XVimMark.h index 0da51de5..881586e9 100644 --- a/XVim/XVimMark.h +++ b/XVim/XVimMark.h @@ -18,4 +18,4 @@ @end -#define XVimMakeMark(line, col, doc) [[[XVimMark alloc] initWithLine:line column:col document:doc] autorelease] \ No newline at end of file +#define XVimMakeMark(line, col, doc) [[[XVimMark alloc] initWithLine:line column:col document:doc.fileURL.path] autorelease] \ No newline at end of file diff --git a/XVim/XVimMarkSetEvaluator.m b/XVim/XVimMarkSetEvaluator.m index 516ba602..61af3420 100644 --- a/XVim/XVimMarkSetEvaluator.m +++ b/XVim/XVimMarkSetEvaluator.m @@ -13,7 +13,7 @@ #import "XVimMark.h" #import "XVimMarks.h" #import "XVim.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" @implementation XVimMarkSetEvaluator @@ -22,18 +22,32 @@ - (XVimKeymap*)selectKeymapWithProvider:(id)keymapProvider { } - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ - NSString* keyStr = [keyStroke toSelectorString]; - if ([keyStr length] != 1) { + XVimView *xview = self.currentView; + XVimBuffer *buffer = self.window.currentBuffer; + + if (keyStroke.modifier) { + return [XVimEvaluator invalidEvaluator]; + } + switch (keyStroke.character) { + case 'a' ... 'z': + case 'A' ... 'Z': + case '0' ... '9': + case '\'': case '"': + case '[': case ']': + break; + default: return [XVimEvaluator invalidEvaluator]; } - - XVimMark* mark = [[[XVimMark alloc] init] autorelease]; - NSRange r = [self.sourceView selectedRange]; - mark.line = [self.sourceView.textStorage xvim_lineNumberAtIndex:r.location]; - mark.column = [self.sourceView.textStorage xvim_columnOfIndex:r.location]; - mark.document = [[self.sourceView documentURL] path]; - if( nil != mark.document ){ - [[XVim instance].marks setMark:mark forName:keyStr]; + + XVimMark *mark = [[[XVimMark alloc] init] autorelease]; + XVimPosition pos = xview.insertionPosition; + + mark.line = pos.line; + mark.column = pos.column; + mark.document = buffer.document.fileURL.path; + if (nil != mark.document) { + unichar c = keyStroke.character; + [[XVim instance].marks setMark:mark forName:[NSString stringWithCharacters:&c length:1]]; } return nil; } diff --git a/XVim/XVimMarks.h b/XVim/XVimMarks.h index 6ffaf83a..eb8d52a2 100644 --- a/XVim/XVimMarks.h +++ b/XVim/XVimMarks.h @@ -51,7 +51,7 @@ * This automatically detects if it is file mark. * If the character is not supported as a mark this returns nil **/ -- (XVimMark*)markForName:(NSString*)name forDocument:(NSString*)documentPath; +- (XVimMark*)markForName:(NSString*)name forDocument:(NSDocument *)document; /** * Set mark. diff --git a/XVim/XVimMarks.m b/XVim/XVimMarks.m index 79f60d98..fd9309d4 100644 --- a/XVim/XVimMarks.m +++ b/XVim/XVimMarks.m @@ -92,7 +92,9 @@ - (NSString*)dumpFileMarks{ } -- (XVimMark*)markForName:(NSString*)name forDocument:(NSString *)documentPath{ +- (XVimMark*)markForName:(NSString*)name forDocument:(NSDocument *)document +{ + NSString *documentPath = document.fileURL.path; NSAssert(nil != name, @"name can not be nil"); NSAssert(nil != documentPath, @"documentPath can not be nil"); diff --git a/XVim/XVimMotion.h b/XVim/XVimMotion.h index 0348ea7f..5263166d 100644 --- a/XVim/XVimMotion.h +++ b/XVim/XVimMotion.h @@ -7,8 +7,7 @@ // #import -#import "XVimMotionType.h" -#import "XVimMotionOption.h" +#import "XVimDefs.h" typedef struct { BOOL reachedEndOfLine; @@ -18,6 +17,14 @@ typedef struct { NSUInteger lastEndOfWord; }XVimMotionInfo; +typedef enum _MOTION_TYPE{ + DEFAULT_MOTION_TYPE, + CHARACTERWISE_INCLUSIVE, + CHARACTERWISE_EXCLUSIVE, + LINEWISE, + BLOCKWISE +} MOTION_TYPE; + #define XVIM_MAKE_MOTION(MOTION,TYPE,OPTION,COUNT) [[[XVimMotion alloc] initWithMotion:MOTION type:TYPE option:OPTION count:COUNT] autorelease] typedef enum _MOTION{ @@ -31,6 +38,7 @@ typedef enum _MOTION{ MOTION_LINE_FORWARD, // k MOTION_LINE_BACKWARD, // j MOTION_END_OF_LINE, // $ + MOTION_COLUMN_OF_LINE, // | MOTION_BEGINNING_OF_LINE, // 0 MOTION_SENTENCE_FORWARD, MOTION_SENTENCE_BACKWARD, @@ -71,15 +79,16 @@ typedef enum _MOTION{ @interface XVimMotion : NSObject @property MOTION motion; @property MOTION_TYPE type; -@property MOTION_OPTION option; +@property XVimMotionOptions option; @property NSUInteger count; +@property (readonly) NSInteger scount; @property NSUInteger line; @property NSUInteger column; @property NSUInteger position; @property unichar character; -@property(strong) NSString* regex; +@property (strong) NSString* regex; @property XVimMotionInfo* info; -- (id) initWithMotion:(MOTION)motion type:(MOTION_TYPE)type option:(MOTION_OPTION)option count:(NSUInteger)count; +- (id) initWithMotion:(MOTION)motion type:(MOTION_TYPE)type option:(XVimMotionOptions)option count:(NSUInteger)count; - (BOOL) isTextObject; @end \ No newline at end of file diff --git a/XVim/XVimMotion.m b/XVim/XVimMotion.m index f24d141e..3e4a5548 100644 --- a/XVim/XVimMotion.m +++ b/XVim/XVimMotion.m @@ -9,8 +9,23 @@ #import "XVimMotion.h" @implementation XVimMotion +@synthesize motion = _motion; +@synthesize type = _type; +@synthesize option = _option; +@synthesize count = _count; +@synthesize line = _line; +@synthesize column = _column; +@synthesize position = _position; +@synthesize character = _character; +@synthesize regex = _regex; +@synthesize info = _info; -- (id) initWithMotion:(MOTION)motion type:(MOTION_TYPE)type option:(MOTION_OPTION)option count:(NSUInteger)count{ +- (NSInteger)scount +{ + return (NSInteger)_count; +} + +- (id) initWithMotion:(MOTION)motion type:(MOTION_TYPE)type option:(XVimMotionOptions)option count:(NSUInteger)count{ if( self = [super init]){ _motion = motion; _type = type; diff --git a/XVim/XVimMotionEvaluator.h b/XVim/XVimMotionEvaluator.h index 2adb9e50..a28c23f6 100644 --- a/XVim/XVimMotionEvaluator.h +++ b/XVim/XVimMotionEvaluator.h @@ -16,9 +16,6 @@ // Make subclass of this to implement operation on which takes motions as argument (deletion,yank...and so on.) @interface XVimMotionEvaluator : XVimNumericEvaluator -@property (strong) XVimMotion* motion; - -- (XVimEvaluator*)commonMotion:(SEL)motion Type:(MOTION_TYPE)type; /** * The difference between motionFixed and _motionFixed: diff --git a/XVim/XVimMotionEvaluator.m b/XVim/XVimMotionEvaluator.m index 603e2563..4b232b74 100644 --- a/XVim/XVimMotionEvaluator.m +++ b/XVim/XVimMotionEvaluator.m @@ -19,7 +19,6 @@ #import "Logger.h" #import "XVimYankEvaluator.h" #import "NSTextStorage+VimOperation.h" -#import "NSString+VimHelper.h" #import "XVimMark.h" #import "XVimMarks.h" #import "XVimCommandLineEvaluator.h" @@ -35,22 +34,18 @@ // How the motion is treated depends on a subclass of the XVimMotionEvaluator. // For example, XVimDeleteEvaluator will delete the letters represented by motion. - - @interface XVimMotionEvaluator() { MOTION_TYPE _forcedMotionType; - BOOL _toggleInclusiveExclusive; + XVimMotion *_motion; } @end @implementation XVimMotionEvaluator -@synthesize motion = _motion; - (id)initWithWindow:(XVimWindow *)window{ self = [super initWithWindow:window]; if (self) { _forcedMotionType = DEFAULT_MOTION_TYPE; - _motion = [XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, 1) retain]; } return self; } @@ -60,36 +55,10 @@ - (void)dealloc{ [super dealloc]; } -// This is helper method commonly used by many key event handlers. -// You do not need to use this if this is not proper to express the motion. -- (XVimEvaluator*)commonMotion:(SEL)motion Type:(MOTION_TYPE)type{ - NSTextView* view = [self sourceView]; - NSUInteger motionTo = (NSUInteger)[view performSelector:motion withObject:[NSNumber numberWithUnsignedInteger:[self numericArg]]]; - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_POSITION, type, MOTION_OPTION_NONE, [self numericArg]); - m.position = motionTo; - return [self _motionFixed:m]; -} - -/* -- (XVimEvaluator*)_motionFixedFrom:(NSUInteger)from To:(NSUInteger)to Type:(MOTION_TYPE)type{ - TRACE_LOG(@"from:%d to:%d type:%d", from, to, type); - if( _forcedMotionType != CHARACTERWISE_EXCLUSIVE){ - if ( type == LINEWISE) { - type = CHARACTERWISE_EXCLUSIVE; - } else if ( type == CHARACTERWISE_EXCLUSIVE ){ - type = CHARACTERWISE_INCLUSIVE; - } else if(type == CHARACTERWISE_INCLUSIVE) { - type = CHARACTERWISE_EXCLUSIVE; - } - } - - XVimEvaluator *ret = [self motionFixedFrom:from To:to Type:type]; - return ret; -} - */ - -(XVimEvaluator*)_motionFixed:(XVimMotion*)motion{ - if( _forcedMotionType == CHARACTERWISE_EXCLUSIVE){ // CHARACTERWISE_EXCLUSIVE means 'v' is pressed and it means toggle inclusive/exclusive. So its not always "exclusive" + if( _forcedMotionType == CHARACTERWISE_EXCLUSIVE){ + // CHARACTERWISE_EXCLUSIVE means 'v' is pressed and it means toggle inclusive/exclusive. + // So its not always "exclusive" if( motion.type == LINEWISE ){ motion.type = CHARACTERWISE_EXCLUSIVE; }else{ @@ -125,11 +94,11 @@ - (XVimEvaluator*)motionFixed:(XVimMotion *)motion{ /////////////////////////////////////////// - (XVimEvaluator*)b{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_WORD_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_WORD_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)B{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_WORD_BACKWARD, CHARACTERWISE_EXCLUSIVE, BIGWORD, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_WORD_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOPT_BIGWORD, [self numericArg])]; } /* @@ -138,12 +107,12 @@ - (XVimEvaluator*)B{ */ - (XVimEvaluator*)e{ - XVimMotion* motion = XVIM_MAKE_MOTION(MOTION_END_OF_WORD_FORWARD, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, [self numericArg]); + XVimMotion* motion = XVIM_MAKE_MOTION(MOTION_END_OF_WORD_FORWARD, CHARACTERWISE_INCLUSIVE, MOPT_NONE, [self numericArg]); return [self _motionFixed:motion]; } - (XVimEvaluator*)E{ - XVimMotion* motion = XVIM_MAKE_MOTION(MOTION_END_OF_WORD_FORWARD, CHARACTERWISE_INCLUSIVE, BIGWORD, [self numericArg]); + XVimMotion* motion = XVIM_MAKE_MOTION(MOTION_END_OF_WORD_FORWARD, CHARACTERWISE_INCLUSIVE, MOPT_BIGWORD, [self numericArg]); return [self _motionFixed:motion]; } @@ -157,25 +126,23 @@ - (XVimEvaluator*)onComplete_fFtT:(XVimArgumentEvaluator*)childEvaluator{ } */ - self.motion.count = self.numericArg; - self.motion.character = childEvaluator.keyStroke.character; - [XVim instance].lastCharacterSearchMotion = self.motion; - return [self _motionFixed:self.motion]; + _motion.count = self.numericArg; + _motion.character = childEvaluator.keyStroke.character; + [XVim instance].lastCharacterSearchMotion = _motion; + return [self _motionFixed:_motion]; } - (XVimEvaluator*)f{ [self.argumentString appendString:@"f"]; self.onChildCompleteHandler = @selector(onComplete_fFtT:); - self.motion.motion = MOTION_NEXT_CHARACTER; - self.motion.type = CHARACTERWISE_INCLUSIVE; + _motion = [XVIM_MAKE_MOTION(MOTION_NEXT_CHARACTER, CHARACTERWISE_INCLUSIVE, MOPT_NONE, 1) retain]; return [[[XVimArgumentEvaluator alloc] initWithWindow:self.window] autorelease]; } - (XVimEvaluator*)F{ [self.argumentString appendString:@"F"]; self.onChildCompleteHandler = @selector(onComplete_fFtT:); - self.motion.motion = MOTION_PREV_CHARACTER; - self.motion.type = CHARACTERWISE_EXCLUSIVE; + _motion = [XVIM_MAKE_MOTION(MOTION_PREV_CHARACTER, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1) retain]; return [[[XVimArgumentEvaluator alloc] initWithWindow:self.window] autorelease]; } @@ -194,8 +161,8 @@ - (XVimEvaluator*)g{ } - (XVimEvaluator*)onComplete_g:(XVimGMotionEvaluator*)childEvaluator{ - if( [childEvaluator.key.toSelectorString isEqualToString:@"SEMICOLON"] ){ - XVimMark* mark = [[XVim instance].marks markForName:@"." forDocument:[self.sourceView documentURL].path]; + if( childEvaluator.key.selector == @selector(SEMICOLON) ){ + XVimMark* mark = [[XVim instance].marks markForName:@"." forDocument:self.window.currentBuffer.document]; return [self jumpToMark:mark firstOfLine:NO]; }else{ return [self _motionFixed:childEvaluator.motion]; @@ -204,41 +171,42 @@ - (XVimEvaluator*)onComplete_g:(XVimGMotionEvaluator*)childEvaluator{ - (XVimEvaluator*)G{ - XVimMotion* m =XVIM_MAKE_MOTION(MOTION_LINENUMBER, LINEWISE, LEFT_RIGHT_NOWRAP, [self numericArg]); - if([self numericMode]){ + XVimMotion *m; + if ([self numericMode]){ + m = XVIM_MAKE_MOTION(MOTION_LINENUMBER, LINEWISE, MOPT_NOWRAP, [self numericArg]); m.line = [self numericArg]; }else{ - m.motion = MOTION_LASTLINE; + m = XVIM_MAKE_MOTION(MOTION_LASTLINE, LINEWISE, MOPT_NOWRAP, [self numericArg]); } return [self _motionFixed:m]; } - (XVimEvaluator*)h{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_BACKWARD, CHARACTERWISE_EXCLUSIVE, LEFT_RIGHT_NOWRAP, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NOWRAP, [self numericArg])]; } - (XVimEvaluator*)H{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_HOME, LINEWISE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_HOME, LINEWISE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)j{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)k{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_LINE_BACKWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_LINE_BACKWARD, LINEWISE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)l{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_FORWARD, CHARACTERWISE_EXCLUSIVE, LEFT_RIGHT_NOWRAP, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_FORWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NOWRAP, [self numericArg])]; } - (XVimEvaluator*)L{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_BOTTOM, LINEWISE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_BOTTOM, LINEWISE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)M{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_MIDDLE, LINEWISE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_MIDDLE, LINEWISE, MOPT_NONE, [self numericArg])]; } @@ -247,7 +215,8 @@ - (XVimEvaluator*)nN_impl:(BOOL)opposite{ if( opposite ){ m.motion = (m.motion == MOTION_SEARCH_FORWARD) ? MOTION_SEARCH_BACKWARD : MOTION_SEARCH_FORWARD; } - self.motion = m; + [_motion release]; + _motion = [m retain]; return [self _motionFixed:m]; } @@ -271,44 +240,40 @@ - (XVimEvaluator*)C_u{ - (XVimEvaluator*)t{ [self.argumentString appendString:@"t"]; self.onChildCompleteHandler = @selector(onComplete_fFtT:); - self.motion.motion = MOTION_TILL_NEXT_CHARACTER; - self.motion.type = CHARACTERWISE_INCLUSIVE; + _motion = [XVIM_MAKE_MOTION(MOTION_TILL_NEXT_CHARACTER, CHARACTERWISE_INCLUSIVE, MOPT_NONE, 1) retain]; return [[[XVimArgumentEvaluator alloc] initWithWindow:self.window] autorelease]; } - (XVimEvaluator*)T{ [self.argumentString appendString:@"T"]; self.onChildCompleteHandler = @selector(onComplete_fFtT:); - self.motion.motion = MOTION_TILL_PREV_CHARACTER; - self.motion.type = CHARACTERWISE_EXCLUSIVE; + _motion = [XVIM_MAKE_MOTION(MOTION_TILL_PREV_CHARACTER, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1) retain]; return [[[XVimArgumentEvaluator alloc] initWithWindow:self.window] autorelease]; } - (XVimEvaluator*)v{ - _forcedMotionType = CHARACTERWISE_EXCLUSIVE; // This does not mean the motion will always be "exclusive". This is just for remembering that its type is "characterwise" forced. - // Actual motion is decided by motions' default inclusive/exclusive attribute and _toggleInclusiveExclusive flag. - _toggleInclusiveExclusive = !_toggleInclusiveExclusive; + // This does not mean the motion will always be "exclusive". + // This is just for remembering that its type is "characterwise" forced. + _forcedMotionType = CHARACTERWISE_EXCLUSIVE; return self; } - (XVimEvaluator*)V{ - _toggleInclusiveExclusive = NO; _forcedMotionType = LINEWISE; return self; } - (XVimEvaluator*)C_v{ - _toggleInclusiveExclusive = NO; _forcedMotionType = BLOCKWISE; return self; } - (XVimEvaluator*)w{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_WORD_FORWARD, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_WORD_FORWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)W{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_WORD_FORWARD, CHARACTERWISE_EXCLUSIVE, BIGWORD, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_WORD_FORWARD, CHARACTERWISE_EXCLUSIVE, MOPT_BIGWORD, [self numericArg])]; } - (XVimEvaluator*)z{ @@ -317,22 +282,26 @@ - (XVimEvaluator*)z{ } - (XVimEvaluator*)NUM0{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_BEGINNING_OF_LINE, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_BEGINNING_OF_LINE, CHARACTERWISE_INCLUSIVE, MOPT_NONE, [self numericArg])]; +} + +- (XVimEvaluator*)BAR{ + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_COLUMN_OF_LINE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)searchCurrentWordForward:(BOOL)forward { XVimCommandLineEvaluator* eval = [self searchEvaluatorForward:forward]; - NSRange r = [self.sourceView xvim_currentWord:MOTION_OPTION_NONE]; + NSRange r = [self.currentView xvim_currentWord:MOPT_NONE]; if( r.location == NSNotFound ){ return nil; } // This is not for matching the searching word itself // Vim also does this behavior( when matched string is not found ) - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_POSITION, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); + XVimMotion* m = XVIM_MAKE_MOTION(MOTION_POSITION, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1); m.position = r.location; - [self.sourceView xvim_move:m]; + [self.currentView moveCursorWithMotion:m]; - NSString* word = [self.sourceView.string substringWithRange:r]; + NSString* word = [self.currentView.buffer.string substringWithRange:r]; NSString* searchWord = [NSRegularExpression escapedPatternForString:word]; searchWord = [NSString stringWithFormat:@"%@%@%@", @"\\b", searchWord, @"\\b"]; [eval appendString:searchWord]; @@ -351,39 +320,40 @@ - (XVimEvaluator*)NUMBER{ // This is internal method used by SQUOTE, BACKQUOTE // TODO: rename firstOfLine -> firstNonblankOfLine - (XVimEvaluator*)jumpToMark:(XVimMark*)mark firstOfLine:(BOOL)fol{ - NSUInteger cur_pos = self.sourceView.insertionPoint; + XVimBuffer *buffer = self.window.currentBuffer; + NSUInteger cur_pos = self.currentView.insertionPoint; MOTION_TYPE motionType = fol?LINEWISE:CHARACTERWISE_EXCLUSIVE; if( mark.line == NSNotFound ){ return [XVimEvaluator invalidEvaluator]; } - if( ![mark.document isEqualToString:self.sourceView.documentURL.path]){ + if( ![mark.document isEqualToString:buffer.document.fileURL.path]){ IDEDocumentController* ctrl = [IDEDocumentController sharedDocumentController]; NSError* error; NSURL* doc = [NSURL fileURLWithPath:mark.document]; [ctrl openDocumentWithContentsOfURL:doc display:YES error:&error]; } - NSUInteger to = [self.sourceView.textStorage xvim_indexOfLineNumber:mark.line column:mark.column]; + NSUInteger to = [buffer indexOfLineNumber:mark.line column:mark.column]; if( NSNotFound == to ){ return [XVimEvaluator invalidEvaluator]; } if( fol ){ - to = [self.sourceView.textStorage xvim_firstNonblankInLineAtIndex:to allowEOL:YES]; // This never returns NSNotFound + to = [buffer firstNonblankInLineAtIndex:to allowEOL:YES]; // This never returns NSNotFound } // set the position before the jump XVimMark* cur_mark = [[[XVimMark alloc] init] autorelease]; - cur_mark.line = [self.sourceView.textStorage xvim_lineNumberAtIndex:cur_pos]; - cur_mark.column = [self.sourceView.textStorage xvim_columnOfIndex:cur_pos]; - cur_mark.document = [self.sourceView documentURL].path; + cur_mark.line = [buffer lineNumberAtIndex:cur_pos]; + cur_mark.column = [buffer columnOfIndex:cur_pos]; + cur_mark.document = buffer.document.fileURL.path; if( nil != mark.document ){ [[XVim instance].marks setMark:cur_mark forName:@"'"]; } - XVimMotion* m =XVIM_MAKE_MOTION(MOTION_POSITION, motionType, MOTION_OPTION_NONE, self.numericArg); + XVimMotion* m =XVIM_MAKE_MOTION(MOTION_POSITION, motionType, MOPT_NONE, self.numericArg); m.position = to; return [self _motionFixed:m]; } @@ -404,7 +374,7 @@ - (XVimEvaluator*)onComplete_SQUOTE:(XVimArgumentEvaluator*)childEvaluator{ // This will work for Ctrl-c as register c but it should not //NSString* key = [childEvaluator.keyStroke toString]; NSString* key = [NSString stringWithFormat:@"%c", childEvaluator.keyStroke.character]; - XVimMark* mark = [[XVim instance].marks markForName:key forDocument:[self.sourceView documentURL].path]; + XVimMark* mark = [[XVim instance].marks markForName:key forDocument:self.window.currentBuffer.document]; return [self jumpToMark:mark firstOfLine:YES]; } @@ -419,45 +389,34 @@ - (XVimEvaluator*)onComplete_BACKQUOTE:(XVimArgumentEvaluator*)childEvaluator{ // This will work for Ctrl-c as register c but it should not // NSString* key = [childEvaluator.keyStroke toString]; NSString* key = [NSString stringWithFormat:@"%c", childEvaluator.keyStroke.character]; - XVimMark* mark = [[XVim instance].marks markForName:key forDocument:[self.sourceView documentURL].path]; + XVimMark* mark = [[XVim instance].marks markForName:key forDocument:self.window.currentBuffer.document]; return [self jumpToMark:mark firstOfLine:NO]; } // CARET ( "^") moves the cursor to the start of the currentline (past leading whitespace) // Note: CARET always moves to start of the current line ignoring any numericArg. - (XVimEvaluator*)CARET{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_FIRST_NONBLANK, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_FIRST_NONBLANK, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)DOLLAR{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_END_OF_LINE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_END_OF_LINE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } // Underscore ( "_") moves the cursor to the start of the line (past leading whitespace) // Note: underscore without any numeric arguments behaves like caret but with a numeric argument greater than 1 // it will moves to start of the numeric argument - 1 lines down. -- (XVimEvaluator*)UNDERSCORE{ - // TODO add this motion interface to NSTextView - NSTextView* view = [self.window sourceView]; - NSRange r = [view selectedRange]; - NSUInteger repeat = self.numericArg; - NSUInteger linesUpCursorloc = [view.textStorage nextLine:r.location column:0 count:(repeat - 1) option:MOTION_OPTION_NONE]; - NSUInteger head = [view.textStorage xvim_firstNonblankInLineAtIndex:linesUpCursorloc allowEOL:NO]; - if( NSNotFound == head && linesUpCursorloc != NSNotFound){ - head = linesUpCursorloc; - }else if(NSNotFound == head){ - head = r.location; - } - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_POSITION, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 0); - m.position = head; - return [self _motionFixed:m]; +- (XVimEvaluator*)UNDERSCORE +{ + NSUInteger repeat = self.numericArg - 1; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_NEXT_FIRST_NONBLANK, LINEWISE, MOPT_NONE, repeat)]; } - (XVimEvaluator*)PERCENT { if( self.numericMode ){ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_PERCENT, LINEWISE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_PERCENT, LINEWISE, MOPT_NONE, [self numericArg])]; }else{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_NEXT_MATCHED_ITEM, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_NEXT_MATCHED_ITEM, CHARACTERWISE_INCLUSIVE, MOPT_NONE, [self numericArg])]; } } @@ -476,17 +435,21 @@ - (XVimEvaluator*)BS{ } - (XVimEvaluator*)PLUS{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_NEXT_FIRST_NONBLANK, LINEWISE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_NEXT_FIRST_NONBLANK, LINEWISE, MOPT_NONE, [self numericArg])]; } + /* - * CR (return) acts like PLUS in vi + * CR / ^M (return) acts like PLUS in vi */ +- (XVimEvaluator *)C_m{ + return [self PLUS]; +} - (XVimEvaluator*)CR{ return [self PLUS]; } - (XVimEvaluator*)MINUS{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_PREV_FIRST_NONBLANK, LINEWISE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_PREV_FIRST_NONBLANK, LINEWISE, MOPT_NONE, [self numericArg])]; } @@ -501,20 +464,20 @@ - (XVimEvaluator*)RSQUAREBRACKET{ } - (XVimEvaluator*)LBRACE{ // { - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_PARAGRAPH_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_PARAGRAPH_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)RBRACE{ // } - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_PARAGRAPH_FORWARD, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_PARAGRAPH_FORWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)LPARENTHESIS{ // ( - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_SENTENCE_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_SENTENCE_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)RPARENTHESIS{ // ) - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_SENTENCE_FORWARD, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_SENTENCE_FORWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)COMMA{ diff --git a/XVim/XVimMotionOption.h b/XVim/XVimMotionOption.h deleted file mode 100644 index fbe4e50c..00000000 --- a/XVim/XVimMotionOption.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// XVimMotionOption.h -// XVim -// -// Created by Tomas Lundell on 10/04/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -typedef enum{ - MOTION_OPTION_NONE = 0x00, - LEFT_RIGHT_WRAP = 0x01, - LEFT_RIGHT_NOWRAP = 0x02, - BIGWORD = 0x04, // for 'WORD' motion - INCLUSIVE = 0x08, - MOPT_PARA_BOUND_BLANKLINE = 0x10, - TEXTOBJECT_INNER = 0x20, - SEARCH_WRAP= 0x40, - SEARCH_CASEINSENSITIVE = 0x80, - MOTION_OPTION_CHANGE_WORD = 0x100, // for 'cw','cW' -} MOTION_OPTION; diff --git a/XVim/XVimMotionType.h b/XVim/XVimMotionType.h deleted file mode 100644 index 941296db..00000000 --- a/XVim/XVimMotionType.h +++ /dev/null @@ -1,8 +0,0 @@ - -typedef enum _MOTION_TYPE{ - DEFAULT_MOTION_TYPE, - CHARACTERWISE_INCLUSIVE, - CHARACTERWISE_EXCLUSIVE, - LINEWISE, - BLOCKWISE -} MOTION_TYPE; diff --git a/XVim/XVimNormalEvaluator.m b/XVim/XVimNormalEvaluator.m index 9ef2bf5f..f361d84f 100644 --- a/XVim/XVimNormalEvaluator.m +++ b/XVim/XVimNormalEvaluator.m @@ -23,7 +23,6 @@ #import "XVimKeyStroke.h" #import "XVimWindow.h" #import "XVim.h" -#import "NSString+VimHelper.h" #import "XVimKeymapProvider.h" #import "Logger.h" #import "XVimCommandLineEvaluator.h" @@ -34,8 +33,7 @@ #import "XVimMark.h" #import "XVimMarks.h" #import "XVimMotion.h" -#import "XVimTildeEvaluator.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "XVimJoinEvaluator.h" @interface XVimNormalEvaluator() { @@ -45,16 +43,9 @@ @interface XVimNormalEvaluator() { @implementation XVimNormalEvaluator --(id)initWithWindow:(XVimWindow *)window{ - self = [super initWithWindow:window]; - if (self) { - } - return self; -} - - (void)becameHandler{ [super becameHandler]; - [self.sourceView xvim_changeSelectionMode:XVIM_VISUAL_NONE]; + self.currentView.selectionMode = XVIM_VISUAL_NONE; } - (NSString*)modeString { @@ -84,8 +75,7 @@ - (XVimEvaluator*)A{ } - (XVimEvaluator*)C_a{ - NSTextView* view = [self sourceView]; - if ([view xvim_incrementNumber:(int64_t)self.numericArg]) { + if ([self.currentView doIncrementNumber:(int64_t)self.numericArg]) { [[XVim instance] fixOperationCommands]; } else { [[XVim instance] cancelOperationCommands]; @@ -95,7 +85,7 @@ - (XVimEvaluator*)C_a{ // This is not motion but scroll. That's the reason the implementation is here. - (XVimEvaluator*)C_b{ - [[self sourceView] xvim_scrollPageBackward:[self numericArg]]; + [self.currentView scrollPageBackward:self.numericArg]; return nil; } @@ -115,7 +105,7 @@ - (XVimEvaluator*)C{ // This is not motion but scroll. That's the reason the implementation is here. - (XVimEvaluator*)C_d{ - [[self sourceView] xvim_scrollHalfPageForward:[self numericArg]]; + [self.currentView scrollHalfPageForward:self.numericArg]; return nil; } @@ -133,28 +123,31 @@ - (XVimEvaluator*)D{ } - (XVimEvaluator*)C_e{ - [[self sourceView] xvim_scrollLineForward:[self numericArg]]; + [self.currentView scrollLineForward:self.numericArg]; return nil; } // This is not motion but scroll. That's the reason the implementation is here. - (XVimEvaluator*)C_f{ - [[self sourceView] xvim_scrollPageForward:[self numericArg]]; + [self.currentView scrollPageForward:self.numericArg]; return nil; } -- (XVimEvaluator*)C_g{ +- (XVimEvaluator*)C_g +{ // process - XVimWindow* window = self.window; - NSRange range = [[window sourceView] selectedRange]; - NSUInteger numberOfLines = [window.sourceView.textStorage xvim_numberOfLines]; - long long lineNumber = [window.sourceView currentLineNumber]; - NSUInteger columnNumber = [window.sourceView.textStorage xvim_columnOfIndex:range.location]; - NSURL* documentURL = [[window sourceView] documentURL]; - if( [documentURL isFileURL] ) { - NSString* filename = [documentURL path]; - NSString* text = [NSString stringWithFormat:@"%@ line %lld of %ld --%d%%-- col %ld", - filename, lineNumber, numberOfLines, (int)((float)lineNumber*100.0/(float)numberOfLines), columnNumber+1 ]; + XVimWindow *window = self.window; + XVimBuffer *buffer = window.currentBuffer; + XVimView *xview = self.currentView; + XVimPosition pos = xview.insertionPosition; + + NSUInteger numberOfLines = [buffer numberOfLines]; + NSURL *documentURL = buffer.document.fileURL; + + if ([documentURL isFileURL]) { + NSString *text = [NSString stringWithFormat:@"%@ line %ld of %ld --%d%%-- col %ld", + documentURL.path, pos.line, numberOfLines, + (int)((CGFloat)pos.line*100.0/(CGFloat)numberOfLines), pos.column + 1 ]; [window statusMessage:text]; } @@ -168,8 +161,8 @@ - (XVimEvaluator*)g{ } - (XVimEvaluator*)onComplete_g:(XVimGActionEvaluator*)childEvaluator{ - if( [childEvaluator.key.toSelectorString isEqualToString:@"SEMICOLON"] ){ - XVimMark* mark = [[XVim instance].marks markForName:@"." forDocument:[self.sourceView documentURL].path]; + if (childEvaluator.key.selector == @selector(SEMICOLON)) { + XVimMark* mark = [[XVim instance].marks markForName:@"." forDocument:self.window.currentBuffer.document]; return [self jumpToMark:mark firstOfLine:NO]; }else{ if( childEvaluator.motion != nil ){ @@ -190,7 +183,7 @@ - (XVimEvaluator*)I{ - (XVimEvaluator*)J{ XVimJoinEvaluator* eval = [[[XVimJoinEvaluator alloc] initWithWindow:self.window addSpace:YES] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, self.numericArg)]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, self.numericArg)]; } // Should be moved to XVimMotionEvaluator @@ -202,14 +195,12 @@ - (XVimEvaluator*)m{ } - (XVimEvaluator*)o{ - NSTextView* view = [self sourceView]; - [view xvim_insertNewlineBelowAndInsertWithIndent]; + [self.currentView insertNewlineBelowAndInsertWithIndent]; return [[[XVimInsertEvaluator alloc] initWithWindow:self.window] autorelease]; } - (XVimEvaluator*)O{ - NSTextView* view = [self sourceView]; - [view xvim_insertNewlineAboveAndInsertWithIndent]; + [self.currentView insertNewlineAboveAndInsertWithIndent]; return [[[XVimInsertEvaluator alloc] initWithWindow:self.window] autorelease]; } @@ -224,17 +215,15 @@ - (XVimEvaluator*)C_i{ } - (XVimEvaluator*)p{ - NSTextView* view = [self sourceView]; XVimRegister* reg = [[[XVim instance] registerManager] registerByName:self.yankRegister]; - [view xvim_put:reg.string withType:reg.type afterCursor:YES count:[self numericArg]]; + [self.currentView doPut:reg.string withType:reg.type afterCursor:YES count:[self numericArg]]; [[XVim instance] fixOperationCommands]; return nil; } - (XVimEvaluator*)P{ - NSTextView* view = [self sourceView]; XVimRegister* reg = [[[XVim instance] registerManager] registerByName:self.yankRegister]; - [view xvim_put:reg.string withType:reg.type afterCursor:NO count:[self numericArg]]; + [self.currentView doPut:reg.string withType:reg.type afterCursor:NO count:[self numericArg]]; [[XVim instance] fixOperationCommands]; return nil; } @@ -262,10 +251,12 @@ - (XVimEvaluator*)onComplete_Recording:childEvaluator{ return nil; } -- (XVimEvaluator*)C_r{ - NSTextView* view = [self sourceView]; - for( NSUInteger i = 0 ; i < [self numericArg] ; i++){ - [view.undoManager redo]; +- (XVimEvaluator*)C_r +{ + XVimBuffer *buffer = self.window.currentBuffer; + + for (NSUInteger i = 0; i < [self numericArg]; i++) { + [buffer.undoManager redo]; } return nil; } @@ -289,17 +280,20 @@ - (XVimEvaluator*)S{ return [d performSelector:@selector(c)]; } -- (XVimEvaluator*)u{ - NSTextView* view = [self sourceView]; - for( NSUInteger i = 0 ; i < [self numericArg] ; i++){ - [view.undoManager undo]; +- (XVimEvaluator*)u +{ + XVimBuffer *buffer = self.window.currentBuffer; + + for (NSUInteger i = 0; i < [self numericArg]; i++) { + [buffer.undoManager undo]; } + [self.currentView moveCursorToIndex:self.currentView.insertionPoint]; return nil; } // This is not motion but scroll. That's the reason the implementation is here. - (XVimEvaluator*)C_u{ - [[self sourceView] xvim_scrollHalfPageBackward:[self numericArg]]; + [self.currentView scrollHalfPageBackward:self.numericArg]; return nil; } @@ -347,9 +341,7 @@ - (XVimEvaluator*)X{ } - (XVimEvaluator*)C_x{ - NSTextView* view = [self sourceView]; - - if ([view xvim_incrementNumber:-(int64_t)self.numericArg]) { + if ([self.currentView doIncrementNumber:-(int64_t)self.numericArg]) { [[XVim instance] fixOperationCommands]; } else { [[XVim instance] cancelOperationCommands]; @@ -369,7 +361,7 @@ - (XVimEvaluator*)y{ } - (XVimEvaluator*)C_y{ - [[self sourceView] xvim_scrollLineBackward:[self numericArg]]; + [self.currentView scrollLineBackward:self.numericArg]; return nil; } @@ -442,7 +434,7 @@ - (XVimEvaluator*)C_RSQUAREBRACKET{ } - (XVimEvaluator*)HT{ - [[self sourceView] xvim_selectNextPlaceholder]; + [self.currentView selectNextPlaceholder]; return nil; } @@ -495,9 +487,10 @@ - (XVimEvaluator*)DOT{ - (XVimEvaluator*)TILDE{ [self.argumentString appendString:@"~"]; - XVimTildeEvaluator* swap = [[[XVimTildeEvaluator alloc] initWithWindow:self.window] autorelease]; - // TODO: support tildeop option - return [swap fixWithNoMotion:self.numericArg]; + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, self.numericArg); + [self.currentView doSwapCharacters:m mode:XVIM_BUFFER_SWAP_CASE]; + [XVim.instance fixOperationCommands]; + return nil; } - (XVimEvaluator*)ForwardDelete{ @@ -513,7 +506,7 @@ -(XVimEvaluator*)Pagedown{ } - (XVimEvaluator*)motionFixed:(XVimMotion *)motion{ - [[self sourceView] xvim_move:motion]; + [self.currentView moveCursorWithMotion:motion]; return nil; } diff --git a/XVim/XVimNumericEvaluator.m b/XVim/XVimNumericEvaluator.m index 708cd221..622924bb 100644 --- a/XVim/XVimNumericEvaluator.m +++ b/XVim/XVimNumericEvaluator.m @@ -8,40 +8,26 @@ #import "XVimNumericEvaluator.h" #import "XVimKeyStroke.h" +#import "NSString+VimHelper.h" @implementation XVimNumericEvaluator - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ - NSString* keyStr = [keyStroke toSelectorString]; - if (keyStroke.isNumeric) { - if (self.numericMode) { - NSString* numStr = [keyStr substringFromIndex:3]; - NSUInteger n = (NSUInteger)[numStr integerValue]; - NSUInteger newHead = self.numericArg; - // prevent integer overflow - if(newHead <= floor((NSUIntegerMax - n) / 10)){ - newHead*=10; - newHead+=n; - self.numericArg = newHead; - [self.argumentString appendString:numStr]; + unichar buf[4] = { 'N', 'U', 'M', keyStroke.character }; + NSUInteger digit = buf[3] - '0'; + + if (self.numericMode || digit) { + NSUInteger n = self.numericMode ? self.numericArg : 0; + + self.numericMode = YES; + if (n <= NSUIntegerMax / 10) { + self.numericArg = 10 * n + digit; + [self.argumentString appendCharacters:buf length:4]; } return self; } - else{ - if( [keyStr isEqualToString:@"NUM0"] ){ - // Nothing to do - // Maybe handled by XVimNormalEvaluator - }else{ - NSString* numStr = [keyStr substringFromIndex:3]; - NSUInteger n = (NSUInteger)[numStr integerValue]; - self.numericArg = n; - [self.argumentString appendString:numStr]; - self.numericMode = YES; - return self; - } - } } return [super eval:keyStroke]; diff --git a/XVim/XVimOperatorEvaluator.h b/XVim/XVimOperatorEvaluator.h index f5bc533b..3feea8d3 100644 --- a/XVim/XVimOperatorEvaluator.h +++ b/XVim/XVimOperatorEvaluator.h @@ -16,7 +16,6 @@ @class XVimOperatorAction; @interface XVimOperatorEvaluator : XVimMotionEvaluator -- (id)initWithWindow:(XVimWindow *)window; - (XVimEvaluator*)executeOperationWithMotion:(XVimMotion*)motion; /* - (XVimEvaluator*)b; diff --git a/XVim/XVimOperatorEvaluator.m b/XVim/XVimOperatorEvaluator.m index 17fb69d4..ea4f6f23 100644 --- a/XVim/XVimOperatorEvaluator.m +++ b/XVim/XVimOperatorEvaluator.m @@ -15,7 +15,10 @@ #import "XVimKeymapProvider.h" #import "XVimMark.h" #import "XVimMarks.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" +#import "XVimYankEvaluator.h" +#import "XVimShiftEvaluator.h" +#import "XVimJoinEvaluator.h" @interface XVimOperatorEvaluator() { } @@ -29,17 +32,11 @@ + (XVimEvaluator*)doOperationWithMotion:(XVimMotion*)motion onView:(NSTextView*) return nil; } -- (id)initWithWindow:window{ - if (self = [super initWithWindow:window]){ - } - return self; -} - - (void)dealloc { [super dealloc]; } -- (float)insertionPointHeightRatio{ +- (CGFloat)insertionPointHeightRatio{ return 0.5; } @@ -71,23 +68,26 @@ - (XVimEvaluator*)_motionFixed:(XVimMotion *)motion{ // We do not fix the change here if next evaluator is not nil becaust it waits more input for fix the command. // This happens for a command like "cw..." if( nil == evaluator ){ - NSTextView* view = self.window.sourceView; - NSString* className = NSStringFromClass([self class]); - if( ![className isEqualToString:@"XVimYankEvaluator"]){ + XVimBuffer *buffer = self.window.currentBuffer; + XVimView *xview = self.currentView; + Class aClass = self.class; + + if (aClass != [XVimYankEvaluator class]) { [[XVim instance] fixOperationCommands]; - XVimMark* mark = nil; - if( [className isEqualToString:@"XVimJoinEvaluator"]){ + XVimMark *mark = nil; + + if (aClass == [XVimJoinEvaluator class]) { // This is specical case for join operation. // The mark is set at the head of next line of the insertion point after the operation - mark = XVimMakeMark([self.sourceView insertionLine]+1, 0, view.documentURL.path); - }else if( [className isEqualToString:@"XVimShiftEvaluator"] ){ - mark = XVimMakeMark([self.sourceView insertionLine], 0, view.documentURL.path); - } - else{ - mark = XVimMakeMark([self.sourceView insertionLine], [self.sourceView insertionColumn], view.documentURL.path); + mark = XVimMakeMark(xview.insertionLine + 1, 0, buffer.document); + } else if (aClass == [XVimShiftEvaluator class]) { + mark = XVimMakeMark(xview.insertionLine, 0, buffer.document); + } else { + XVimPosition pos = xview.insertionPosition; + mark = XVimMakeMark(pos.line, pos.column, buffer.document); } - if( nil != mark.document){ + if (nil != mark.document) { [[XVim instance].marks setMark:mark forName:@"."]; } } diff --git a/XVim/XVimOptions.h b/XVim/XVimOptions.h index 2d03e5f1..1fa6c322 100644 --- a/XVim/XVimOptions.h +++ b/XVim/XVimOptions.h @@ -17,7 +17,7 @@ @property BOOL smartcase; @property BOOL debug; @property BOOL hlsearch; -@property BOOL number; +@property (nonatomic) BOOL number; @property (copy) NSString *clipboard; @property (copy) NSString *guioptions; @property (copy) NSString *timeoutlen; diff --git a/XVim/XVimOptions.m b/XVim/XVimOptions.m index d2e67034..5bb7fce8 100644 --- a/XVim/XVimOptions.m +++ b/XVim/XVimOptions.m @@ -16,6 +16,20 @@ @interface XVimOptions() { @end @implementation XVimOptions +@synthesize ignorecase = _ignorecase; +@synthesize wrapscan = _wrapscan; +@synthesize errorbells = _errorbells; +@synthesize incsearch = _incsearch; +@synthesize gdefault = _gdefault; +@synthesize smartcase = _smartcase; +@synthesize debug = _debug; +@synthesize hlsearch = _hlsearch; +@synthesize number = _number; +@synthesize clipboard = _clipboard; +@synthesize guioptions = _guioptions; +@synthesize timeoutlen = _timeoutlen; +@synthesize laststatus = _laststatus; +@synthesize vimregex = _vimregex; - (id)init{ if( self = [super init] ){ diff --git a/XVim/XVimRecordingEvaluator.m b/XVim/XVimRecordingEvaluator.m index 9408e90a..ca338223 100644 --- a/XVim/XVimRecordingEvaluator.m +++ b/XVim/XVimRecordingEvaluator.m @@ -18,6 +18,8 @@ @interface XVimRecordingEvaluator() @end @implementation XVimRecordingEvaluator +@synthesize reg = _reg, evaluatorStack = _evaluatorStack; + - (id)initWithWindow:(XVimWindow *)window withRegister:(NSString*)reg{ if( self = [super initWithWindow:window] ){ self.evaluatorStack = [[[NSMutableArray alloc] init] autorelease]; @@ -48,15 +50,15 @@ - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ return self; } -- (float)insertionPointHeightRatio{ +- (CGFloat)insertionPointHeightRatio{ return [[self.evaluatorStack lastObject] insertionPointHeightRatio]; } -- (float)insertionPointWidthRatio{ +- (CGFloat)insertionPointWidthRatio{ return [[self.evaluatorStack lastObject] insertionPointWidthRatio]; } -- (float)insertionPointAlphaRatio{ +- (CGFloat)insertionPointAlphaRatio{ return [[self.evaluatorStack lastObject] insertionPointAlphaRatio]; } diff --git a/XVim/XVimRegister.h b/XVim/XVimRegister.h index b346c0e3..54709a51 100644 --- a/XVim/XVimRegister.h +++ b/XVim/XVimRegister.h @@ -7,14 +7,9 @@ // #import +#import "XVimDefs.h" #import "XVimKeyStroke.h" -typedef enum{ - TEXT_TYPE_CHARACTERS, - TEXT_TYPE_BLOCK, - TEXT_TYPE_LINES -}TEXT_TYPE; - @interface XVimRegister : NSObject -(void) appendXVimString:(XVimString*)string; -(void) setXVimString:(XVimString*)string; diff --git a/XVim/XVimRegister.m b/XVim/XVimRegister.m index f42eb5eb..c1362318 100644 --- a/XVim/XVimRegister.m +++ b/XVim/XVimRegister.m @@ -19,6 +19,7 @@ @interface XVimRegister() @end @implementation XVimRegister +@synthesize type = _type, string = _string; - (id)init{ if(self = [super init]){ @@ -124,6 +125,10 @@ @interface XVimRegisterManager() @implementation XVimRegisterManager +@synthesize registers = _registers; +@synthesize recordingRegister = _recordingRegister; +@synthesize recordingRegisterName = _recordingRegisterName; +@synthesize lastExecutedRegister = _lastExecutedRegister; static const NSString* s_enum_registers = @"\"0123456789abcdefghijklmnopqrstuvwxyz-*.:%/+~"; diff --git a/XVim/XVimRegisterEvaluator.m b/XVim/XVimRegisterEvaluator.m index 8b44ddd5..bfa49201 100644 --- a/XVim/XVimRegisterEvaluator.m +++ b/XVim/XVimRegisterEvaluator.m @@ -19,14 +19,15 @@ @interface XVimRegisterEvaluator() { @end @implementation XVimRegisterEvaluator +@synthesize reg; - (XVimKeymap*)selectKeymapWithProvider:(id)keymapProvider { return [keymapProvider keymapForMode:XVIM_MODE_NONE]; } - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ - SEL handler = [keyStroke selectorForInstance:self]; - if (handler){ + SEL handler = keyStroke.selector; + if ([self respondsToSelector:handler]) { TRACE_LOG(@"Calling SELECTOR %@", NSStringFromSelector(handler)); return [self performSelector:handler]; } diff --git a/XVim/XVimSearch.m b/XVim/XVimSearch.m index 2bddcd42..8a504219 100644 --- a/XVim/XVimSearch.m +++ b/XVim/XVimSearch.m @@ -7,16 +7,24 @@ // #import "XVimSearch.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "NSString+VimHelper.h" #import "XVimWindow.h" #import "XVim.h" #import "XVimOptions.h" #import "Logger.h" #import "XVimUtil.h" -#import "IDEKit.h" +#import "NSTextStorage+VimOperation.h" @implementation XVimSearch +@synthesize lastSearchBackword = _lastSearchBackword; +@synthesize lastSearchCase = _lastSearchCase; +@synthesize lastSearchCmd = _lastSearchCmd; +@synthesize lastSearchString = _lastSearchString; +@synthesize lastSearchDisplayString = _lastSearchDisplayString; +@synthesize lastReplacementString = _lastReplacementString; +@synthesize matchStart = _matchStart; +@synthesize matchEnd = _matchEnd; - (id)init { if( self = [super init] ){ @@ -38,10 +46,10 @@ - (XVimMotion*)motionForSearch:(NSString *)string forward:(BOOL)forward{ XVimMotion* m = nil; if( forward ){ XVim.instance.searcher.lastSearchBackword = NO; - m = XVIM_MAKE_MOTION(MOTION_SEARCH_FORWARD, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); + m = XVIM_MAKE_MOTION(MOTION_SEARCH_FORWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1); }else{ XVim.instance.searcher.lastSearchBackword = YES; - m = XVIM_MAKE_MOTION(MOTION_SEARCH_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1); + m = XVIM_MAKE_MOTION(MOTION_SEARCH_BACKWARD, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1); } m.regex = string; @@ -60,11 +68,11 @@ - (XVimMotion*)motionForSearch:(NSString *)string forward:(BOOL)forward{ } // The last case sensitiveness is found at this point if( options & NSRegularExpressionCaseInsensitive ){ - m.option |= SEARCH_CASEINSENSITIVE; + m.option |= MOPT_SEARCH_CASEINSENSITIVE; } if( [XVim instance].options.wrapscan ){ - m.option |= SEARCH_WRAP; + m.option |= MOPT_SEARCH_WRAP; } return m; @@ -153,7 +161,7 @@ - (NSRange)searchForwardFrom:(NSUInteger)from inWindow:(XVimWindow*)window{ #ifdef __MAC_10_7 XVimOptions *options = [[XVim instance] options]; - NSTextView* srcView = [window sourceView]; + NSTextView* srcView = window.currentView.textView; NSUInteger search_base = from; NSRegularExpressionOptions r_opts = NSRegularExpressionAnchorsMatchLines | NSRegularExpressionUseUnicodeWordBoundaries; @@ -223,7 +231,7 @@ - (NSRange)searchBackwardFrom:(NSUInteger)from inWindow:(XVimWindow*)window NSRange found = {NSNotFound, 0}; #ifdef __MAC_10_7 XVimOptions *options = [[XVim instance] options]; - NSTextView* srcView = [window sourceView]; + NSTextView* srcView = window.currentView.textView; NSUInteger search_base = from; NSRegularExpressionOptions r_opts = NSRegularExpressionAnchorsMatchLines; @@ -307,7 +315,7 @@ - (NSRange)searchCurrentWordFrom:(NSUInteger)from forward:(BOOL)forward matchWho { NSRange found = {NSNotFound,0}; #ifdef __MAC_10_7 - NSTextView *view = [window sourceView]; + NSTextView *view = window.currentView.textView; NSRange begin = [view selectedRange]; NSString *string = [view string]; @@ -315,7 +323,7 @@ - (NSRange)searchCurrentWordFrom:(NSUInteger)from forward:(BOOL)forward matchWho NSUInteger firstNonblank = NSNotFound; // TODO: must be moved to NSTextStorage+VimOperation - for (NSUInteger i = begin.location; ![view.textStorage isEOF:i]; ++i) + for (NSUInteger i = begin.location; i < string.length; ++i) { unichar curChar = [string characterAtIndex:i]; if (isNewline(curChar)){ @@ -349,11 +357,11 @@ - (NSRange)searchCurrentWordFrom:(NSUInteger)from forward:(BOOL)forward matchWho unichar lastChar = [string characterAtIndex:wordStart-1]; if ((isKeyword(curChar) && isKeyword(lastChar)) || (!isKeyword(curChar) && isNonblank(curChar) && !isKeyword(lastChar) && isNonblank(lastChar))){ - wordStart = [view.textStorage wordsBackward:searchStart count:1 option:LEFT_RIGHT_NOWRAP]; + wordStart = [view.textStorage wordsBackward:searchStart count:1 option:MOPT_NOWRAP]; } } - NSUInteger wordEnd = [view.textStorage wordsForward:wordStart count:1 option:LEFT_RIGHT_NOWRAP info:&info]; + NSUInteger wordEnd = [view.textStorage wordsForward:wordStart count:1 option:MOPT_NOWRAP info:&info]; if (info.lastEndOfWord != NSNotFound){ wordEnd = info.lastEndOfWord; } @@ -395,7 +403,7 @@ - (NSRange)replaceForwardFrom:(NSUInteger)from to:(NSUInteger)to inWindow:(XVimW #ifdef __MAC_10_7 - NSTextView* srcView = [window sourceView]; + NSTextView* srcView = window.currentView.textView; NSRegularExpressionOptions r_opts = NSRegularExpressionAnchorsMatchLines|NSRegularExpressionUseUnicodeWordBoundaries; if ([self isCaseInsensitive]) @@ -466,17 +474,18 @@ - (void)substitute:(NSString*)ex_command from:(NSUInteger)from to:(NSUInteger)to self.lastSearchCmd = replaced; self.lastSearchDisplayString = replaced; self.lastReplacementString = replacement; - + + XVimBuffer *buffer = window.currentBuffer; // Find the position to start searching - NSUInteger replace_start_location = [window.sourceView.textStorage xvim_indexOfLineNumber:from column:0]; + NSUInteger replace_start_location = [buffer indexOfLineNumber:from column:0]; if( NSNotFound == replace_start_location){ return; } // Find the position to end the searching - NSUInteger endOfReplacement = [window.sourceView.textStorage xvim_indexOfLineNumber:to+1 column:0]; // Next line of the end of range. + NSUInteger endOfReplacement = [buffer indexOfLineNumber:to+1 column:0]; // Next line of the end of range. if( NSNotFound == endOfReplacement ){ - endOfReplacement = [[[window sourceView] string] length]; + endOfReplacement = buffer.length; } // This is lazy implementation. // When text is substituted the end location may be smaller or greater than original end position. diff --git a/XVim/XVimShiftEvaluator.m b/XVim/XVimShiftEvaluator.m index 86eb1f5b..cd71d8d2 100644 --- a/XVim/XVimShiftEvaluator.m +++ b/XVim/XVimShiftEvaluator.m @@ -7,7 +7,7 @@ // #import "XVimShiftEvaluator.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" #import "XVimWindow.h" #import "XVim.h" @@ -31,7 +31,7 @@ - (XVimEvaluator*)GREATERTHAN { return nil; } - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg]-1); + XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOPT_NONE, [self numericArg]-1); return [self _motionFixed:m]; } return nil; @@ -42,18 +42,15 @@ - (XVimEvaluator*)LESSTHAN{ if ([self numericArg] < 1) return nil; - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg]-1); + XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOPT_NONE, [self numericArg]-1); return [self _motionFixed:m]; } return nil; } -- (XVimEvaluator*)motionFixed:(XVimMotion *)motion{ - if( _unshift ){ - [[self sourceView] xvim_shiftLeft:motion]; - }else{ - [[self sourceView] xvim_shiftRight:motion]; - } +- (XVimEvaluator*)motionFixed:(XVimMotion *)motion +{ + [self.currentView doShift:motion right:!_unshift]; return nil; } @end diff --git a/XVim/XVimStatusLine.m b/XVim/XVimStatusLine.m index 3a97338e..4d18cbc7 100644 --- a/XVim/XVimStatusLine.m +++ b/XVim/XVimStatusLine.m @@ -14,12 +14,13 @@ #import #import "XVim.h" #import "XVimOptions.h" +#import "XVimBuffer.h" #define STATUS_LINE_HEIGHT 18 @interface XVimStatusLine () -- (void)_documentChangedNotification:(NSNotification *)notification; +- (void)_bufferChangedNotification:(NSNotification *)notification; @end @@ -39,7 +40,7 @@ - (id)initWithFrame:(NSRect)frame _status.backgroundColor = [NSColor clearColor]; [_status setEditable:NO]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_documentChangedNotification:) name:XVimDocumentChangedNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_bufferChangedNotification:) name:XVimBufferChangedNotification object:nil]; [self addSubview:_background]; [self addSubview:_status]; @@ -98,11 +99,13 @@ - (void)didContainerFrameChanged:(NSNotification*)notification{ } -- (void)_documentChangedNotification:(NSNotification *)notification +- (void)_bufferChangedNotification:(NSNotification *)notification { - NSString *documentPath = [[notification userInfo] objectForKey:XVimDocumentPathKey]; - if (documentPath != nil) { - [_status setString:documentPath]; + XVimBuffer *buffer = [notification.userInfo objectForKey:XVimBufferKey]; + NSString *path = buffer.document.fileURL.path; + + if (path) { + [_status setString:path]; } } diff --git a/XVim/XVimStringBuffer.h b/XVim/XVimStringBuffer.h index 856c9b78..630f9024 100644 --- a/XVim/XVimStringBuffer.h +++ b/XVim/XVimStringBuffer.h @@ -8,6 +8,8 @@ #import +#define XVIM_STRING_BUFFER_SIZE 64 + /** @brief structure used for fast search in an NSString * * This has complex invariants: @@ -26,50 +28,53 @@ typedef struct xvim_string_buffer_s { NSUInteger s_index; // index of buffer[0] in s NSUInteger b_index; // index in buffer being read NSUInteger b_len; // number of characters read in buffer - unichar buffer[64]; + unichar buffer[XVIM_STRING_BUFFER_SIZE]; } xvim_string_buffer_t; #define XVimInvalidChar ((unichar)-1) -NS_INLINE NSUInteger _xvim_sb_size(void) -{ - return sizeof(((xvim_string_buffer_t *)NULL)->buffer) / sizeof(unichar); -} - NS_INLINE void _xvim_sb_load(xvim_string_buffer_t *sb) { - NSUInteger len = MIN(sb->s_max - sb->s_index, _xvim_sb_size()); + NSUInteger len = MIN(sb->s_max - sb->s_index, XVIM_STRING_BUFFER_SIZE); sb->b_len = len; - NSCAssert(sb->b_len <= _xvim_sb_size(), @"b_len is bogus"); + NSCAssert(sb->b_len <= XVIM_STRING_BUFFER_SIZE, @"b_len is bogus"); if (len > 0) { [sb->s getCharacters:sb->buffer range:NSMakeRange(sb->s_index, len)]; } - if (len < _xvim_sb_size()) { + if (len < XVIM_STRING_BUFFER_SIZE) { sb->buffer[len] = XVimInvalidChar; } } /* returns NO if at end */ -NS_INLINE void xvim_sb_init(xvim_string_buffer_t *sb, NSString *s, NSUInteger index, NSRange forRange) +NS_INLINE void xvim_sb_init(xvim_string_buffer_t *sb, NSString *s, + NSUInteger index, NSUInteger min, NSUInteger max) { sb->s = s; - sb->s_min = forRange.location; - sb->s_max = sb->s_min + forRange.length; + sb->s_min = min; + sb->s_max = max; - NSCAssert(index >= sb->s_min && index <= sb->s_max, @"bad caller"); + NSCAssert(min <= max, @"Bad xvim_sb_init"); + NSCAssert(index >= sb->s_min && index <= sb->s_max, @"bad xvim_sb_init"); + NSCAssert(max <= s.length, @"bad xvim_sb_init"); - if (forRange.length < _xvim_sb_size() || index - _xvim_sb_size() / 2 < sb->s_min) { + if (max - min < XVIM_STRING_BUFFER_SIZE || index - XVIM_STRING_BUFFER_SIZE / 2 < sb->s_min) { sb->s_index = sb->s_min; - } else if (index + _xvim_sb_size() >= sb->s_max) { - sb->s_index = sb->s_max - _xvim_sb_size() + 1; + } else if (index + XVIM_STRING_BUFFER_SIZE >= sb->s_max) { + sb->s_index = sb->s_max - XVIM_STRING_BUFFER_SIZE + 1; } else { - sb->s_index = index - _xvim_sb_size() / 2; + sb->s_index = index - XVIM_STRING_BUFFER_SIZE / 2; } sb->b_index = index - sb->s_index; _xvim_sb_load(sb); } +NS_INLINE void xvim_sb_init_range(xvim_string_buffer_t *sb, NSString *s, NSRange range) +{ + xvim_sb_init(sb, s, range.location, range.location, NSMaxRange(range)); +} + NS_INLINE NSUInteger xvim_sb_index(xvim_string_buffer_t *sb) { return sb->s_index + sb->b_index; @@ -87,7 +92,7 @@ NS_INLINE NSRange xvim_sb_range_to_end(xvim_string_buffer_t *sb) NS_INLINE unichar xvim_sb_peek_prev(xvim_string_buffer_t *sb) { - return sb->b_index > 0 ? XVimInvalidChar : sb->buffer[sb->b_index]; + return sb->b_index == 0 ? XVimInvalidChar : sb->buffer[sb->b_index - 1]; } NS_INLINE unichar xvim_sb_peek(xvim_string_buffer_t *sb) @@ -115,11 +120,11 @@ NS_INLINE BOOL xvim_sb_next(xvim_string_buffer_t *sb) if (sb->b_index < sb->b_len) { return YES; } - if (sb->b_len < _xvim_sb_size()) { + if (sb->b_len < XVIM_STRING_BUFFER_SIZE) { return NO; } - sb->s_index += _xvim_sb_size() / 2; - sb->b_index = _xvim_sb_size() / 2; + sb->s_index += XVIM_STRING_BUFFER_SIZE / 2; + sb->b_index = XVIM_STRING_BUFFER_SIZE / 2; _xvim_sb_load(sb); return sb->b_index < sb->b_len; } @@ -132,7 +137,7 @@ NS_INLINE BOOL xvim_sb_prev(xvim_string_buffer_t *sb) return YES; } - NSUInteger diff = MIN(sb->s_index - sb->s_min, _xvim_sb_size() / 2); + NSUInteger diff = MIN(sb->s_index - sb->s_min, XVIM_STRING_BUFFER_SIZE / 2); if (diff > 0) { sb->s_index -= diff; sb->b_index = diff; diff --git a/XVim/XVimLowercaseEvaluator.h b/XVim/XVimSwapCharsEvaluator.h similarity index 60% rename from XVim/XVimLowercaseEvaluator.h rename to XVim/XVimSwapCharsEvaluator.h index 9bf1dab7..aa9c386a 100644 --- a/XVim/XVimLowercaseEvaluator.h +++ b/XVim/XVimSwapCharsEvaluator.h @@ -8,5 +8,6 @@ #import "XVimOperatorEvaluator.h" -@interface XVimLowercaseEvaluator : XVimOperatorEvaluator +@interface XVimSwapCharsEvaluator : XVimOperatorEvaluator +- (instancetype)initWithWindow:(XVimWindow *)window mode:(int)mode; @end diff --git a/XVim/XVimSwapCharsEvaluator.m b/XVim/XVimSwapCharsEvaluator.m new file mode 100644 index 00000000..19e1146e --- /dev/null +++ b/XVim/XVimSwapCharsEvaluator.m @@ -0,0 +1,65 @@ +// +// XVimLowercaseEvaluator.m +// XVim +// +// Created by Tomas Lundell on 6/04/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import "XVimSwapCharsEvaluator.h" +#import "XVimWindow.h" +#import "XVim.h" + +@implementation XVimSwapCharsEvaluator { + int _mode; +} + +- (instancetype)initWithWindow:(XVimWindow *)window mode:(int)mode +{ + if ((self = [self initWithWindow:window])) { + _mode = mode; + } + return self; +} + +- (XVimEvaluator *)_doitLineWiseIfModeIs:(int)mode +{ + if (_mode != mode || [self numericArg] < 1) + return nil; + + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOPT_NONE, [self numericArg]-1); + return [self _motionFixed:m]; +} + +- (XVimEvaluator *)u +{ + [self.argumentString appendString:@"u"]; + return [self _doitLineWiseIfModeIs:XVIM_BUFFER_SWAP_LOWER]; +} + +- (XVimEvaluator *)U +{ + [self.argumentString appendString:@"U"]; + return [self _doitLineWiseIfModeIs:XVIM_BUFFER_SWAP_UPPER]; +} + +- (XVimEvaluator *)QUESTION +{ + [self.argumentString appendString:@"?"]; + return [self _doitLineWiseIfModeIs:XVIM_BUFFER_SWAP_ROT13]; +} + +- (XVimEvaluator *)TILDE +{ + [self.argumentString appendString:@"~"]; + return [self _doitLineWiseIfModeIs:XVIM_BUFFER_SWAP_CASE]; +} + +- (XVimEvaluator *)motionFixed:(XVimMotion*)motion +{ + [self.currentView doSwapCharacters:motion mode:_mode]; + return nil; +} + + +@end diff --git a/XVim/XVimTester.m b/XVim/XVimTester.m index ee550345..243746d9 100644 --- a/XVim/XVimTester.m +++ b/XVim/XVimTester.m @@ -79,6 +79,7 @@ @interface XVimTester(){ @implementation XVimTester +@synthesize testCases; - (id)init{ if( self = [super init] ){ @@ -278,7 +279,7 @@ -(NSInteger) getIndexOfNthFailingTestcase:(NSInteger)nth{ return retval; } -- (float)heightForString:(NSString*)myString withFont:(NSFont*)myFont withWidth:(float)myWidth{ +- (CGFloat)heightForString:(NSString*)myString withFont:(NSFont*)myFont withWidth:(CGFloat)myWidth{ NSTextStorage *textStorage = [[[NSTextStorage alloc] initWithString:myString] autorelease]; NSTextContainer *textContainer = [[[NSTextContainer alloc] initWithContainerSize:NSMakeSize(myWidth, FLT_MAX)] autorelease]; NSLayoutManager *layoutManager = [[[NSLayoutManager alloc] init] autorelease]; @@ -300,7 +301,7 @@ - (CGFloat)tableView:(NSTableView *)tv heightOfRow:(NSInteger)row{ NSCell* cell = (NSCell*)[column dataCell]; font = [NSFont fontWithName:@"Menlo" size:13]; [cell setFont:font]; // FIXME: This should not be done here. - float width = column.width; + CGFloat width = column.width; NSString* msg; if(showPassing){ msg = ((XVimTestCase*)[self.testCases objectAtIndex:(NSUInteger)row]).message; @@ -311,7 +312,7 @@ - (CGFloat)tableView:(NSTableView *)tv heightOfRow:(NSInteger)row{ if( nil == msg || [msg isEqualToString:@""] ){ msg = @" "; } - float ret = [self heightForString:msg withFont:font withWidth:width]; + CGFloat ret = [self heightForString:msg withFont:font withWidth:width]; return ret + 10; } return 13.0; diff --git a/XVim/XVimTextObjectEvaluator.m b/XVim/XVimTextObjectEvaluator.m index b5b8d691..7359f502 100644 --- a/XVim/XVimTextObjectEvaluator.m +++ b/XVim/XVimTextObjectEvaluator.m @@ -8,7 +8,6 @@ #import "XVimWindow.h" #import "XVimKeyStroke.h" #import "XVimKeymapProvider.h" -#import "XVimMotionOption.h" @interface XVimTextObjectEvaluator() { BOOL _inner; @@ -32,8 +31,8 @@ - (void)dealloc{ } - (XVimMotion *)motion { - MOTION_OPTION opt = _inner ? TEXTOBJECT_INNER : MOTION_OPTION_NONE; - opt |= _bigword ? BIGWORD : MOTION_OPTION_NONE; + XVimMotionOptions opt = _inner ? MOPT_TEXTOBJECT_INNER : MOPT_NONE; + opt |= _bigword ? MOPT_BIGWORD : MOPT_NONE; return XVIM_MAKE_MOTION(_textobject, CHARACTERWISE_INCLUSIVE, opt, [self numericArg]); } @@ -52,7 +51,7 @@ - (void)didEndHandler [super didEndHandler]; } -- (float)insertionPointHeightRatio{ +- (CGFloat)insertionPointHeightRatio{ return 0.5; } diff --git a/XVim/XVimTextStoring.h b/XVim/XVimTextStoring.h index d1a55419..6fd89740 100644 --- a/XVim/XVimTextStoring.h +++ b/XVim/XVimTextStoring.h @@ -6,89 +6,28 @@ // // -#import +#import -/** @brief Protocol that must be implemented by the NSTextStorage you intend to hook - * - * Note that the terms here do not have the usual Cocoah meaning - * - * "Character" - * Character is a one unichar value. (any value including tabs,spaces) - * - * "index" - * This is the 0-based location of a given character within -xvim_string. - * - * "Position" - * This is an XVimPosition (line + column) - * - * "EOF" - * EOF is the position of the end of the document (-length), - * so for a text of "abc", EOF is just after the 'c', at position 3. - * - * What we have to think about is that a cursor can be at EOF, - * but -[xvim_string characterAtIndex:] at this position raises. - * - * We have to be careful about it when computing motions effects. - * - * "Newline" - * Newline is defined as "unichar determined by isNewline function". - * Usually "\n" or "\r". - * - * "Line" - * Line is a sequence of characters terminated by newline or EOF. - * "Line" includes the last newline character. - * Line numbers start at 1 - * - * "Blankline" - * Blankline is a line which has only newline or EOF. - * In other words, it is newline character or EOF after newline character. - * - * "Last of Line(LOL)" - * Last of line is the last character of a line EXCLUDING newline character. - * This means that blankline does NOT have an Last of line. - * - * "First of Line(FOL)" - * First of line is the first character of a line excluding newline character. - * This means that blankline does NOT have a First of line. - * - * "First Nonblank of Line" - * First Nonblank of Line is the first printable character in a line. - * - * "End of Line(EOL)" - * End of Line is newline or EOF character at the end of a line. - * A line always has an EOL. - * - * "Beginning of Line (BOL)" - * First character of a line including newline and EOF - * - ****************************************************************************** - * - * Implementation notes for developpers +@class XVimBuffer; + +/** @brief Protocol that can be implemented by your NSTextStorage. * - * XVim provides a default implementation of this protocol - * on an NSTextStorage Category. + * Implementing it will likely boost XVimBuffer performance significantly + * All selectors are optional, and serve as the direct backend for + * XVimBuffer selectors of the same name without the xvim_ prefix. * - * The functions that are reused to implement other of the protocol are marked - * as "XVIM PRIMITIVE" which means that if you want to adapt for a given - * NSTextStorage subclass, those are the only one you probably need to - * consider for reimplementing. */ @protocol XVimTextStoring -@property (nonatomic, readonly) NSString *xvim_string; +@optional -@property (nonatomic, readonly) NSUInteger xvim_numberOfLines; +#pragma mark *** Content & Lines access *** -#pragma mark Settings - -@property (nonatomic, readonly) NSUInteger xvim_tabWidth; -@property (nonatomic, readonly) NSUInteger xvim_indentWidth; +@property (nonatomic, readonly) NSString *xvim_string; -#pragma mark Converting between Indexes and Line Numbers +@property (nonatomic, readonly) NSUInteger xvim_numberOfLines; /** @brief returns the index range for the given line number - * - * XVIM PRIMITIVE * * @param[in] num * The line number @@ -101,8 +40,6 @@ - (NSRange)xvim_indexRangeForLineNumber:(NSUInteger)num newLineLength:(NSUInteger *)newLineLength; /** @brief returns the index range for the given line range - * - * XVIM PRIMITIVE * * @param[in] range the line range. * @@ -112,29 +49,7 @@ */ - (NSRange)xvim_indexRangeForLines:(NSRange)range; -/** @brief returns the line range around the given index - * - * XVIM PRIMITIVE - * - * @param[in] index - * The index within -xvim_string - * @param[out] newLineLength - * The number of characters after the returned range forming the end of line - * @returns - * the range of indexes forming the line, exclugint trailing newLine characters - * Note that if the index is within a CRLF for example, the range may end before index - */ -- (NSRange)xvim_indexRangeForLineAtIndex:(NSUInteger)index newLineLength:(NSUInteger *)newLineLength; - -/** @brief starting position of line @a num within -xvim_string. - * @returns the starting index for that line number or NSNotFound. - * @see -xvim_indexRangeForLineNumber:newLineLength: - */ -- (NSUInteger)xvim_indexOfLineNumber:(NSUInteger)num; - -/** @brief get the line number of a given position. - * - * XVIM PRIMITIVE +/** @brief get the line number of a given index. * * @returns * the line number of specified index. @@ -142,123 +57,12 @@ */ - (NSUInteger)xvim_lineNumberAtIndex:(NSUInteger)index; -#pragma mark Converting between Indexes and Line Numbers + Columns - -/** @brief returns the column number of \a index within the line. - * - * Column numbers starts at 0. - * - * This never returns NSNotFound. - */ -- (NSUInteger)xvim_columnOfIndex:(NSUInteger)index; - -/** @brief returns number of columns for the line containing \a index. - * - * If the specified line does not exist in the current document it returns NSNotFound - */ -- (NSUInteger)xvim_numberOfColumnsInLineAtIndex:(NSUInteger)index; - -/** @brief returns the index for the given line number and column. - * - * @returns - * NSNotFound if \a num exceeds the number of lines in the document - * If \a column is larger than the number of columns in that line, - * it returns the index of the endOfLine for that line - */ -- (NSUInteger)xvim_indexOfLineNumber:(NSUInteger)num column:(NSUInteger)column; - -#pragma mark Searching particular positions on the current line - -/** @brief position of the first character of the line containing \a index. - * - * @param index the index to search backwards from - */ -- (NSUInteger)xvim_startOfLine:(NSUInteger)index; // never returns NSNotFound - -/** @brief returns the firstOfLine for the line containing \a index. - * - * If the line is blank, this returns NSNotFound - * else this is the same as -beginningOfLine: - */ -- (NSUInteger)xvim_firstOfLine:(NSUInteger)index; // May return NSNotFound - -/** @brief position of the end of the line containing \a index. - * - * @param index the index to search from - * - * @returns - * the position of the end of the line. - * end of the line is either: - * - a newline character at the end of the line - * - end of the document - * - * Note that for files with \r\n if index points to \n - * this returns a position before index. - */ -- (NSUInteger)xvim_endOfLine:(NSUInteger)index; // never returns NSNotFound - -/** @brief returns the lastOfLine for the line containing \a index. - * - * If the line is blank, this returns NSNotFound - * else this is the same as -endOfLine:index - 1 - */ -- (NSUInteger)xvim_lastOfLine:(NSUInteger)index; // May return NSNotFound - -/** @brief returns the next non blank position on the same line. - * - * @param index the index to search from - * @param allowEOL whether reaching EOL is allowed or not - * - * @returns - * the position of the first non blank character, starting at index. - * - * if \a allowEOL is NO and that no non blank character is found, - * this returns NSNotFound. - */ -- (NSUInteger)xvim_nextNonblankInLineAtIndex:(NSUInteger)index allowEOL:(BOOL)allowEOL; +#pragma mark *** Indent *** -/** @brief returns the first non blank character on the line, possibly EOL. - * - * @param index searches on the line containing that index. - * - * @returns - * the position of the first non blank character - * on the line containing \a index. - * - * if \a allowEOL is NO and that no non blank character is found, - * this returns NSNotFound. - */ -- (NSUInteger)xvim_firstNonblankInLineAtIndex:(NSUInteger)index allowEOL:(BOOL)allowEOL; +@property (nonatomic, readonly) NSUInteger xvim_tabWidth; -/** @brief returns the next digit position on the same line. - * - * @param index the index to search from - * - * @returns - * the position of the first decimal digit character starting at \a index - * - * this returns NSNotFound if none is found. - */ -- (NSUInteger)xvim_nextDigitInLine:(NSUInteger)index; +@property (nonatomic, readonly) NSUInteger xvim_indentWidth; +- (void)xvim_indentCharacterRange:(NSRange)range buffer:(XVimBuffer *)buffer; @end - -typedef NSTextStorage XVimTextStorage; - -#pragma mark Macros - -#ifdef DEBUG -// The methods here often take index as current interest position and index can be at EOF -// The following macros asserts the range of index. -// WITH_EOF permits the index at EOF position. -// WITHOUT_EOF doesn't permit the index at EOF position. -#define ASSERT_VALID_RANGE_WITH_EOF(x) NSAssert( x <= [self length] || [self length] == 0, @"index can not exceed the length of string. TextLength:%lu SpecifiedIndex:%lu", (long)[self length], x) - -// Some methods assume that "index" is at valid cursor position in Normal mode. -// See isValidCursorPosition's description the condition of the valid cursor position. -#define ASSERT_VALID_CURSOR_POS(x) NSAssert( [self isValidCursorPosition:x], @"index can not be invalid cursor position" ) -#else -#define ASSERT_VALID_RANGE_WITH_EOF(x) -#define ASSERT_VALID_CURSOR_POS(x) -#endif diff --git a/XVim/XVimTextViewProtocol.h b/XVim/XVimTextViewProtocol.h deleted file mode 100644 index 3585a1e1..00000000 --- a/XVim/XVimTextViewProtocol.h +++ /dev/null @@ -1,113 +0,0 @@ -// -// XVimTextView.h -// XVim - -// -// Created by Suzuki Shuichiro on 9/19/12. -// -// - -#import "XVim.h" -#import "XVimMotion.h" -#import "XVimRegister.h" -#import - -/** - * This is the interface to operate on text view used in XVim. - * Text views want to communicate with XVim handlers(evaluators) must implement this protocol. - **/ - -@protocol XVimTextViewDelegateProtocol -- (void)textView:(NSTextView*)view didYank:(NSString*)yankedText withType:(TEXT_TYPE)type; -- (void)textView:(NSTextView*)view didDelete:(NSString*)deletedText withType:(TEXT_TYPE)type; -@end - -typedef enum { - CURSOR_MODE_INSERT, - CURSOR_MODE_COMMAND -}CURSOR_MODE; - -typedef enum { - OPTION_NONE, -}OPERATION_OPTION; - -@protocol XVimTextViewProtocol -@property(readonly) NSUInteger insertionPoint; -@property(readonly) NSUInteger insertionColumn; -@property(readonly) NSUInteger insertionLine; -@property(readonly) NSUInteger selectionBegin; -@property(readonly) XVIM_VISUAL_MODE selectionMode; -@property(readonly) NSUInteger selectedLines; // Number of lines selected (0 if not in selection mode) -@property(readonly) NSUInteger selectedColumns; // Number of columns selected (0 if not in selection mode) -@property(readonly) CURSOR_MODE cursorMode; -@property(readonly) NSUInteger preservedColumn; - -// Selection Mode -- (void)changeSelectionMode:(XVIM_VISUAL_MODE)mode; - - - - -// Top Level Operation Interface -- (void)move:(XVimMotion*)motion; -- (void)delete:(XVimMotion*)motion; -- (void)change:(XVimMotion*)motion; -- (void)yank:(XVimMotion*)motion; -- (void)put:(NSString*)text withType:(TEXT_TYPE)type afterCursor:(bool)after count:(NSUInteger)times; -- (void)swapCase:(XVimMotion*)motion; // Previously this is named "toggleCase" in XVim -- (void)makeLowerCase:(XVimMotion*)motion; // Previously this is named "lowerCase" in XVim -- (void)makeUpperCase:(XVimMotion*)motion; // Previously this is named "lowerCase" in XVim -- (void)filter:(XVimMotion*)motion; -- (void)shiftRight:(XVimMotion*)motion; -- (void)shiftLeft:(XVimMotion*)motion; -- (void)join:(NSUInteger)count; -- (void)insertNewlineBelowLine:(NSUInteger)line; -- (void)insertNewlineAboveLine:(NSUInteger)line; -- (void)insertNewlineAbove; -- (void)insertNewlineBelow; -- (void)insertNewlineAboveAndInsert; -- (void)insertNewlineBelowAndInsert; -- (void)insertText:(NSString*)str line:(NSUInteger)line column:(NSUInteger)column; -- (BOOL)replaceCharacters:(unichar)c count:(NSUInteger)count; - -// Insert or Command -- (void)escapeFromInsert; -- (void)append; -- (void)insert; -- (void)appendAtEndOfLine; -- (void)insertBeforeFirstNonblank; - -// for keydown in insertion -- (void)passThroughKeyDown:(NSEvent*)event; - -// Premitive Operations ( Avoid using following. Consider use or make Top Level Operation Interface instead ) -- (void)moveBack:(NSUInteger)count option:(MOTION_OPTION)opt; -- (void)moveFoward:(NSUInteger)count option:(MOTION_OPTION)opt; -- (void)moveDown:(NSUInteger)count option:(MOTION_OPTION)opt; -- (void)moveUp:(NSUInteger)count option:(MOTION_OPTION)opt; -//- (void)wordsForward:(NSUInteger)index count:(NSUInteger)count option:(MOTION_OPTION)opt info:(XVimWordInfo*)info; -- (void)moveWordsBackward:(NSUInteger)count option:(MOTION_OPTION)opt; -- (void)moveSentencesForward:(NSUInteger)count option:(MOTION_OPTION)opt; -- (void)moveSentencesBackward:(NSUInteger)count option:(MOTION_OPTION)opt; -- (void)moveParagraphsForward:(NSUInteger)count option:(MOTION_OPTION)opt; -- (void)moveParagraphsBackward:(NSUInteger)count option:(MOTION_OPTION)opt; - -- (void)overwriteCharacter:(unichar)c; - - -- (void)moveToPosition:(XVimPosition)pos; - -// Scrolls -- (void)scrollPageForward:(NSUInteger)count; -- (void)scrollPageBackward:(NSUInteger)count; -- (void)scrollHalfPageForward:(NSUInteger)count; -- (void)scrollHalfPageBackward:(NSUInteger)count; -- (void)scrollLineForward:(NSUInteger)count; -- (void)scrollLineBackward:(NSUInteger)count; -- (void)scrollBottom:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb; -- (void)scrollTop:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb;; -- (void)scrollCenter:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb;; - - - -@end diff --git a/XVim/XVimTildeEvaluator.h b/XVim/XVimTildeEvaluator.h deleted file mode 100644 index 7faff2c0..00000000 --- a/XVim/XVimTildeEvaluator.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// XVimTildeEvaluator.h -// XVim -// -// Created by Tomas Lundell on 6/04/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import "XVimOperatorEvaluator.h" - -@interface XVimTildeEvaluator : XVimOperatorEvaluator -- (XVimEvaluator*)fixWithNoMotion:(NSUInteger)count; -@end diff --git a/XVim/XVimTildeEvaluator.m b/XVim/XVimTildeEvaluator.m deleted file mode 100644 index 28768ccd..00000000 --- a/XVim/XVimTildeEvaluator.m +++ /dev/null @@ -1,33 +0,0 @@ -// -// XVimTildeEvaluator.m -// XVim -// -// Created by Tomas Lundell on 6/04/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import "XVimTildeEvaluator.h" -#import "XVimWindow.h" -#import "NSTextView+VimOperation.h" -#import "XVim.h" - -@implementation XVimTildeEvaluator - -- (XVimEvaluator*)fixWithNoMotion:(NSUInteger)count{ - return [self _motionFixed:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, count)]; -} - -- (XVimEvaluator*)TILDE{ - if ([self numericArg] < 1) - return nil; - - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg]-1); - return [self _motionFixed:m]; -} - --(XVimEvaluator*)motionFixed:(XVimMotion*)motion{ - [[self sourceView] xvim_swapCase:motion]; - return nil; -} - -@end \ No newline at end of file diff --git a/XVim/XVimUndo.h b/XVim/XVimUndo.h new file mode 100644 index 00000000..8c409179 --- /dev/null +++ b/XVim/XVimUndo.h @@ -0,0 +1,29 @@ +// +// XVimUndo.h +// XVim +// +// Created by John AppleSeed on 17/11/13. +// +// + +#import + +@class XVimBuffer; +@class XVimView; + +@interface XVimUndoOperation : NSObject + +- (instancetype)initWithIndex:(NSUInteger)index; + +- (void)undoRedo:(XVimBuffer *)buffer view:(XVimView *)view; + +- (void)addUndoRange:(NSRange)range + replacementRange:(NSRange)replacementRange + buffer:(XVimBuffer *)buffer; + +- (void)setStartIndex:(NSUInteger)index; +- (void)setEndIndex:(NSUInteger)index; + +- (void)registerForBuffer:(XVimBuffer *)buffer; + +@end \ No newline at end of file diff --git a/XVim/XVimUndo.m b/XVim/XVimUndo.m new file mode 100644 index 00000000..112218bc --- /dev/null +++ b/XVim/XVimUndo.m @@ -0,0 +1,126 @@ +// +// XVimUndo.m +// XVim +// +// Created by John AppleSeed on 17/11/13. +// +// + +#import "XVimUndo.h" +#import "XVimView.h" +#import "XVimBuffer.h" + +@interface NSTextView (NSPrivate) +/* Cocoa Internal: + * Cocoa seems to call that when -undo is called, + * presumably to prevent the view from refreshing. + */ +- (void)_setUndoRedoInProgress:(BOOL)arg1; +@end + +@interface NSTextStorage (NSUndo) +/* Cocoa Internal: + * Looking at a class dump, this NSUndo category looks intersting + * and breaking on it in a debugger shows that it's called to capture + * the text being replaced in the undo operation, let's mimic that. + */ +- (id)_undoRedoAttributedSubstringFromRange:(NSRange)arg1; +@end + +@implementation XVimUndoOperation { +@protected + NSUndoManager *_undoManager; + NSUInteger _startIndex; + NSUInteger _endIndex; + /* index 3 * i + 0: initial range + * index 3 * i + 1: replacement range + * index 3 * i + 2: text + */ + NSMutableArray *_values; +} + +- (instancetype)initWithIndex:(NSUInteger)index +{ + if ((self = [super init])) { + _startIndex = index; + _endIndex = index; + } + return self; +} + +- (void)dealloc +{ + [_undoManager release]; + [_values release]; + [super dealloc]; +} + +- (void)setStartIndex:(NSUInteger)index +{ + if (_startIndex == NSNotFound) { + _startIndex = index; + } +} + +- (void)setEndIndex:(NSUInteger)index +{ + _endIndex = index; +} + +- (void)addUndoRange:(NSRange)range + replacementRange:(NSRange)replacementRange + buffer:(XVimBuffer *)buffer +{ + if (!_values) { + _values = [[NSMutableArray alloc] initWithCapacity:3]; + } + [_values addObject:[NSValue valueWithRange:range]]; + [_values addObject:[NSValue valueWithRange:replacementRange]]; + [_values addObject:[buffer.textStorage _undoRedoAttributedSubstringFromRange:range]]; +} + +- (void)_undoOp:(NSUInteger)i textStorage:(NSTextStorage *)ts range:(NSRange)range +{ + NSUInteger index= 3 * i + 2; + NSAttributedString *oldText = [_values objectAtIndex:index]; + NSAttributedString *newText = [ts _undoRedoAttributedSubstringFromRange:range]; + + [ts replaceCharactersInRange:range withAttributedString:oldText]; + [_values replaceObjectAtIndex:index withObject:newText]; +} + +- (void)undoRedo:(XVimBuffer *)buffer view:(XVimView *)xview +{ + NSTextStorage *ts = buffer.textStorage; + NSTextView *view = xview.textView; + NSUInteger count = _values.count / 3; + + [view _setUndoRedoInProgress:YES]; + if (!view || [view shouldChangeTextInRange:NSMakeRange(NSNotFound, 0) replacementString:@""]) { + [ts beginEditing]; + if ([_undoManager isUndoing]) { + for (NSUInteger i = count; i-- > 0; ) { + [self _undoOp:i textStorage:ts range:[[_values objectAtIndex:3 * i + 1] rangeValue]]; + } + } else { + for (NSUInteger i = 0; i < count; i ++ ) { + [self _undoOp:i textStorage:ts range:[[_values objectAtIndex:3 * i + 0] rangeValue]]; + } + } + [ts endEditing]; + [self registerForBuffer:buffer]; + } + NSUInteger index = [_undoManager isUndoing] ? _startIndex : _endIndex; + if (index != NSNotFound) { + [xview moveCursorToIndex:index]; + } + [view _setUndoRedoInProgress:NO]; +} + +- (void)registerForBuffer:(XVimBuffer *)buffer +{ + _undoManager = [buffer.undoManager retain]; + [_undoManager registerUndoWithTarget:buffer selector:@selector(undoRedo:) object:self]; +} + +@end diff --git a/XVim/XVimUppercaseEvaluator.h b/XVim/XVimUppercaseEvaluator.h deleted file mode 100644 index 3306446a..00000000 --- a/XVim/XVimUppercaseEvaluator.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// XVimUppercaseEvaluator.h -// XVim -// -// Created by Tomas Lundell on 6/04/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import "XVimOperatorEvaluator.h" - -@interface XVimUppercaseEvaluator : XVimOperatorEvaluator -@end - diff --git a/XVim/XVimUppercaseEvaluator.m b/XVim/XVimUppercaseEvaluator.m deleted file mode 100644 index ef6819d7..00000000 --- a/XVim/XVimUppercaseEvaluator.m +++ /dev/null @@ -1,29 +0,0 @@ -// -// XVimUppercaseEvaluator.m -// XVim -// -// Created by Tomas Lundell on 6/04/12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import "XVimUppercaseEvaluator.h" -#import "XVimWindow.h" -#import "XVim.h" -#import "NSTextView+VimOperation.h" - -@implementation XVimUppercaseEvaluator - -- (XVimEvaluator*)U{ - if ([self numericArg] < 1) - return nil; - - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg]-1); - return [self _motionFixed:m]; -} - --(XVimEvaluator*)motionFixed:(XVimMotion*)motion{ - [[self sourceView] xvim_makeUpperCase:motion]; - return nil; -} - -@end diff --git a/XVim/XVimView.h b/XVim/XVimView.h new file mode 100644 index 00000000..5a01f73b --- /dev/null +++ b/XVim/XVimView.h @@ -0,0 +1,135 @@ +// +// XVimView.h +// XVim +// +// Created by John AppleSeed on 21/11/13. +// +// + +#import +#import "XVimDefs.h" +#import "XVimMotion.h" + +@class XVimView, XVimBuffer, XVimWindow; + +@interface NSTextView (XVimView) +@property (readonly, nonatomic) XVimView *xvim_view; + +- (XVimView *)xvim_makeXVimViewInWindow:(XVimWindow *)window; +@end + +/** + * This is the interface to operate on text view used in XVim. + * Text views want to communicate with XVim handlers(evaluators) must implement this protocol. + **/ + +@protocol XVimTextViewDelegateProtocol +- (void)textView:(NSTextView*)view didYank:(NSString*)yankedText withType:(TEXT_TYPE)type; +- (void)textView:(NSTextView*)view didDelete:(NSString*)deletedText withType:(TEXT_TYPE)type; +@end + +/** @brief XVimView class, representing one XVim View + * + * An XVim View is more or less what a window is in vim, namely a view + * on a given buffer (TextStorage). You can have several views on the same buffer. + * + * This is actually grafted on top of an NSTextView that it hooks to give it + * vim-like capabilities. + */ +@interface XVimView : NSObject +@property (readonly, nonatomic) XVimWindow *window; +@property (readonly, nonatomic) NSTextView *textView; +@property (readonly, nonatomic) XVimBuffer *buffer; +@property (strong, nonatomic) id delegate; + +- (instancetype)initWithView:(NSTextView *)view window:(XVimWindow *)window; + +#pragma mark *** Properties *** + +@property (readonly, nonatomic) NSUInteger insertionPoint; +@property (readonly, nonatomic) XVimPosition insertionPosition; +@property (readonly, nonatomic) NSUInteger insertionColumn; +@property (readonly, nonatomic) NSUInteger insertionLine; + +@property (readonly, nonatomic) NSUInteger selectionBegin; +@property (readonly, nonatomic) XVimPosition selectionPosition; +@property (readonly, nonatomic) NSUInteger selectionColumn; +@property (readonly, nonatomic) NSUInteger selectionLine; + +@property (nonatomic) XVimVisualMode selectionMode; +@property (readonly, nonatomic) BOOL inVisualMode; /* != VISUAL_NONE */ +@property (readonly, nonatomic) BOOL inBlockMode; /* == VISUAL_BLOCK */ + +@property (nonatomic, readonly) BOOL needsUpdateFoundRanges; +@property (nonatomic, readonly) NSArray *foundRanges; + +#pragma mark *** Visual Mode and Cursor Position *** + +- (void)escapeFromInsertAndMoveBack:(BOOL)moveBack; + +- (void)selectSwapCorners:(BOOL)onSameLine; + +- (void)saveVisualInfoForBuffer:(XVimBuffer *)buffer; + +- (void)selectNextPlaceholder; +- (void)selectPreviousPlaceholder; +- (void)adjustCursorPosition; + +- (void)moveCursorToIndex:(NSUInteger)index; +- (void)moveCursorToPosition:(XVimPosition)index; +- (void)moveCursorWithMotion:(XVimMotion *)motion; + +#pragma mark *** Operations *** + +- (void)doDelete:(XVimMotion *)motion andYank:(BOOL)yank; +- (void)doChange:(XVimMotion *)motion; +- (void)doYank:(XVimMotion*)motion; +- (void)doPut:(NSString *)text withType:(TEXT_TYPE)type afterCursor:(bool)after count:(NSUInteger)count; +- (void)doSwapCharacters:(XVimMotion *)motion mode:(int)mode; +- (BOOL)doReplaceCharacters:(unichar)c count:(NSUInteger)count; +- (void)doJoin:(NSUInteger)count addSpace:(BOOL)addSpace; +- (void)doFilter:(XVimMotion *)motion; +- (void)doShift:(XVimMotion *)motion right:(BOOL)right; +- (void)insertNewlineAboveAndInsertWithIndent; +- (void)insertNewlineBelowAndInsertWithIndent; +- (void)doInsert:(XVimInsertionPoint)mode blockColumn:(NSUInteger *)column blockLines:(XVimRange *)lines; +- (BOOL)doIncrementNumber:(int64_t)offset; +- (void)doInsertFixupWithText:(NSString *)text mode:(XVimInsertionPoint)mode + count:(NSUInteger)count column:(NSUInteger)column lines:(XVimRange)lines; +- (void)doSortLines:(XVimRange)range withOptions:(XVimSortOptions)options; + +#pragma mark *** Drawing *** + +- (NSUInteger)lineNumberInScrollView:(CGFloat)ratio offset:(NSInteger)offset; + +- (void)drawInsertionPointInRect:(NSRect)rect color:(NSColor *)color + heightRatio:(CGFloat)heightRatio + widthRatio:(CGFloat)widthRatio + alpha:(CGFloat)alpha; + +#pragma mark *** Scrolling *** + +- (void)scrollBottom:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb; +- (void)scrollCenter:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb; +- (void)scrollTop:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb; +- (void)scrollTo:(NSUInteger)location; +- (void)scrollPageForward:(NSUInteger)count; +- (void)scrollPageBackward:(NSUInteger)count; +- (void)scrollHalfPageForward:(NSUInteger)count; +- (void)scrollHalfPageBackward:(NSUInteger)count; +- (void)scrollLineForward:(NSUInteger)count; +- (void)scrollLineBackward:(NSUInteger)count; + +#pragma mark *** Crap to sort *** + +- (void)xvim_setWrapsLines:(BOOL)wraps; +- (void)xvim_hideCompletions; +- (void)xvim_highlightNextSearchCandidate:(NSString *)regex count:(NSUInteger)count + option:(XVimMotionOptions)opt forward:(BOOL)forward; +- (void)xvim_highlightNextSearchCandidateForward:(NSString*)regex count:(NSUInteger)count option:(XVimMotionOptions)opt; +- (void)xvim_highlightNextSearchCandidateBackward:(NSString*)regex count:(NSUInteger)count option:(XVimMotionOptions)opt; +- (void)xvim_updateFoundRanges:(NSString*)pattern withOption:(XVimMotionOptions)opt; +- (void)xvim_clearHighlightText; +- (NSRange)xvim_currentWord:(XVimMotionOptions)opt; + +@end diff --git a/XVim/XVimView.m b/XVim/XVimView.m new file mode 100644 index 00000000..23054ac4 --- /dev/null +++ b/XVim/XVimView.m @@ -0,0 +1,2405 @@ +// +// XVimView.m +// XVim +// +// Created by John AppleSeed on 21/11/13. +// +// + +// FIXME: layering issue +#if XVIM_XCODE_VERSION == 5 +#define __XCODE5__ +#endif +#define __USE_DVTKIT__ +#import "DVTKit.h" +#import "XVimUndo.h" +#import "NSTextStorage+VimOperation.h" +// END FIXME + +#import +#import "Logger.h" +#import "NSObject+XVimAdditions.h" +#import "NSString+VimHelper.h" +#import "Utils.h" +#import "XVim.h" +#import "XVimMotion.h" +#import "XVimOptions.h" +#import "XVimSearch.h" +#import "XVimView.h" +#import "XVimWindow.h" + + +@interface XVimView () +@property (nonatomic, readwrite) BOOL needsUpdateFoundRanges; + +- (NSRect)_glyphRectAtIndex:(NSUInteger)index length:(NSUInteger)length; +- (void)_syncStateFromView; + +@end + +static char const * const XVIM_KEY_VIEW = "xvim_view"; + +@implementation NSTextView (XVimView) + ++ (void)xvim_initialize +{ + if (self == [NSTextView class]) { + DEBUG_LOG("Swizzling NSTextView"); + +#define swizzle(sel) \ + [self xvim_swizzleInstanceMethod:@selector(sel) with:@selector(xvim_##sel)] + + swizzle(setSelectedRanges:affinity:stillSelecting:); + swizzle(selectAll:); + swizzle(paste:); + swizzle(delete:); + swizzle(keyDown:); + swizzle(mouseDown:); + swizzle(drawRect:); + swizzle(_drawInsertionPointInRect:color:); + swizzle(drawInsertionPointInRect:color:turnedOn:); + swizzle(didChangeText); + swizzle(viewDidMoveToSuperview); + } + +#undef swizzle +} + +- (XVimView *)xvim_view +{ + return objc_getAssociatedObject(self, XVIM_KEY_VIEW); +} + +- (XVimView *)xvim_makeXVimViewInWindow:(XVimWindow *)window +{ + return [[[XVimView alloc] initWithView:self window:window] autorelease]; +} + +- (void)xvim_setSelectedRanges:(NSArray *)ranges + affinity:(NSSelectionAffinity)affinity + stillSelecting:(BOOL)flag +{ + [self xvim_setSelectedRanges:ranges affinity:affinity stillSelecting:flag]; + if (self.xvim_view && !XVim.instance.disabled) { + [self.xvim_view _syncStateFromView]; + } +} + +- (void)xvim_selectAll:(id)sender +{ + [self xvim_selectAll:sender]; + if (!XVim.instance.disabled) { + [self.xvim_view.window syncEvaluatorStack]; + } +} + +- (void)xvim_paste:(id)sender +{ + [self xvim_paste:sender]; + if (!XVim.instance.disabled) { + [self.xvim_view.window syncEvaluatorStack]; + } +} + +- (void)xvim_delete:(id)sender +{ + [self xvim_delete:sender]; + if (!XVim.instance.disabled) { + [self.xvim_view.window syncEvaluatorStack]; + } +} + +- (void)xvim_keyDown:(NSEvent *)theEvent +{ + XVimWindow *window = self.xvim_view.window; + + if (!window || XVim.instance.disabled) { + return [self xvim_keyDown:theEvent]; + } + + @try { + TRACE_LOG(@"Event:%@, XVimNotation:%@", theEvent.description, XVimKeyNotationFromXVimString([theEvent toXVimString])); + if ([window handleKeyEvent:theEvent]) { + [self updateInsertionPointStateAndRestartTimer:YES]; + return; + } + // Call Original keyDown: + [self xvim_keyDown:theEvent]; + } + @catch (NSException* exception) { + ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); + [Logger logStackTrace:exception]; + } +} + +- (void)xvim_mouseDown:(NSEvent *)theEvent +{ + XVimWindow *window = self.xvim_view.window; + + [self xvim_mouseDown:theEvent]; + + if (!window || XVim.instance.disabled) { + return; + } + + @try { + TRACE_LOG(@"Event:%@", theEvent.description); + + // When mouse down, NSTextView ( base in this case) takes the control of event loop internally + // and the method call above does not return immidiately and block until mouse up. mouseDragged: method is called from inside it but + // it never calls mouseUp: event. After mouseUp event is handled internally it returns the control. + // So the code here is executed AFTER mouseUp event is handled. + // At this point NSTextView changes its selectedRange so we usually have to sync XVim state. + + // TODO: To make it simple we should forward mouse events + // to handleKeyStroke as a special key stroke + // and the key stroke should be handled by the current evaluator. + + [window syncEvaluatorStack]; + } + @catch (NSException* exception) { + ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); + [Logger logStackTrace:exception]; + } +} + +- (void)xvim_drawRect:(NSRect)dirtyRect +{ + XVimView *xview = self.xvim_view; + + if (XVim.instance.disabled || !xview) { + return [self xvim_drawRect:dirtyRect]; + } + + @try { + if (XVim.instance.options.hlsearch) { + XVimMotion *lastSearch = [XVim.instance.searcher motionForRepeatSearch]; + + if (nil != lastSearch.regex) { + [xview xvim_updateFoundRanges:lastSearch.regex withOption:lastSearch.option]; + } + } else { + [xview xvim_clearHighlightText]; + } + + [self xvim_drawRect:dirtyRect]; + + if (xview.inVisualMode) { + // NSTextView does not draw insertion point when selecting text. + // We have to draw insertion point by ourselves. + NSRect glyphRect = [xview _glyphRectAtIndex:xview.insertionPoint length:1]; + [[self.insertionPointColor colorWithAlphaComponent:0.5] set]; + NSRectFillUsingOperation(glyphRect, NSCompositeSourceOver); + } + } + @catch (NSException* exception) { + ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); + [Logger logStackTrace:exception]; + } +} + +// Drawing Caret +- (void)xvim__drawInsertionPointInRect:(NSRect)aRect color:(NSColor*)aColor +{ + XVimWindow *window = self.xvim_view.window; + + if (!window || XVim.instance.disabled) { + return [self xvim__drawInsertionPointInRect:aRect color:aColor]; + } + + @try { + // We do not call original _darawInsertionPointRect here + // Because it uses NSRectFill to draw the caret which overrides the character entirely. + // We want some tranceparency for the caret. + + // Call our drawing method + [window drawInsertionPointInRect:aRect color:aColor]; + + } + @catch (NSException* exception) { + ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); + [Logger logStackTrace:exception]; + } +} + +- (void)xvim_drawInsertionPointInRect:(NSRect)rect color:(NSColor *)color turnedOn:(BOOL)flag +{ + [self xvim_drawInsertionPointInRect:rect color:color turnedOn:flag]; + + if (!flag && self.xvim_view && !XVim.instance.disabled) { + // Then tell the view to redraw to clear a caret. + [self setNeedsDisplay:YES]; + } +} + +- (void)xvim_didChangeText +{ + if (self.xvim_view && !XVim.instance.disabled) { + self.xvim_view.needsUpdateFoundRanges = YES; + } + [self xvim_didChangeText]; +} + +- (void)xvim_viewDidMoveToSuperview +{ + if (!self.xvim_view || XVim.instance.disabled) { + return [self xvim_viewDidMoveToSuperview]; + } + + @try { + // Hide scroll bars according to options + [self xvim_viewDidMoveToSuperview]; + [self.enclosingScrollView setPostsBoundsChangedNotifications:YES]; + } + @catch (NSException* exception) { + ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); + [Logger logStackTrace:exception]; + } +} + +@end + +@implementation XVimView { + NSTextView *__unsafe_unretained _textView; + + NSUInteger _preservedColumn; + BOOL _syncStateLock; + CURSOR_MODE _cursorMode; + + NSString *_lastYankedText; + TEXT_TYPE _lastYankedType; + + NSMutableArray *_foundRanges; +} +@synthesize window = _window; +@synthesize textView = _textView; +@synthesize delegate = _delegate; + +@synthesize insertionPoint = _insertionPoint; +@synthesize selectionBegin = _selectionBegin; +@synthesize selectionMode = _selectionMode; + +@synthesize needsUpdateFoundRanges = _needsUpdateFoundRanges; +@synthesize foundRanges = _foundRanges; + ++ (void)initialize +{ + if (self == [XVimView class]) { + [NSTextView xvim_initialize]; + } +} + +- (instancetype)initWithView:(NSTextView *)view window:(XVimWindow *)window +{ + if ((self = [super init])) { + DEBUG_LOG("View %p created for %@", self, view); + + _textView = view; + _window = [window retain]; + + [self _xvim_statusChanged:nil]; + + [XVim.instance.options addObserver:self forKeyPath:@"hlsearch" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; + [XVim.instance.options addObserver:self forKeyPath:@"ignorecase" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; + [XVim.instance.searcher addObserver:self forKeyPath:@"lastSearchString" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(_xvim_statusChanged:) + name:XVimEnabledStatusChangedNotification object:nil]; + + objc_setAssociatedObject(view, XVIM_KEY_VIEW, self, OBJC_ASSOCIATION_RETAIN); + } + return self; +} + +- (void)dealloc +{ + DEBUG_LOG("View %p deleted", self); + @try { + [XVim.instance.options removeObserver:self forKeyPath:@"hlsearch"]; + [XVim.instance.options removeObserver:self forKeyPath:@"ignorecase"]; + [XVim.instance.searcher removeObserver:self forKeyPath:@"lastSearchString"]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + } + @catch (NSException* exception){ + ERROR_LOG(@"Exception %@: %@", [exception name], [exception reason]); + [Logger logStackTrace:exception]; + } + [_foundRanges release]; + [_lastYankedText release]; + [_window release]; + [super dealloc]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object + change:(NSDictionary *)change context:(void *)context +{ + if ([@[ @"ignorecase", @"hlsearch", @"lastSearchString"] containsObject:keyPath]) { + _needsUpdateFoundRanges = YES; + [_textView setNeedsDisplayInRect:_textView.visibleRect avoidAdditionalLayout:YES]; + } +} + +- (void)_xvim_statusChanged:(id)sender +{ + if (!XVim.instance.disabled) { + [self _syncStateFromView]; + [self scrollTo:_insertionPoint]; + } + [_textView setNeedsDisplay:YES]; +} + +- (void)_moveCursor:(NSUInteger)pos preserveColumn:(BOOL)preserve +{ + XVimBuffer *buffer = self.buffer; + NSUInteger length = buffer.length; + + // This method only update the internal state(like _insertionPoint) + if (pos > length) { + ERROR_LOG(@"Position specified exceeds the length of the text"); + pos = length; + } + + _insertionPoint = pos; + if (_cursorMode == CURSOR_MODE_COMMAND && _selectionMode == XVIM_VISUAL_NONE) { + if (![buffer isNormalCursorPositionValidAtIndex:pos]) { + _insertionPoint = pos - 1; + } + } + + if (!preserve) { + _preservedColumn = [buffer columnOfIndex:_insertionPoint]; + } + + DEBUG_LOG(@"New Insertion Point:%d Preserved Column:%d", _insertionPoint, _preservedColumn); +} + +- (void)_syncStateFromView +{ + // TODO: handle block selection (if selectedRanges have multiple ranges ) + if (_syncStateLock || self.buffer.isEditing) { + return; + } + + NSRange r = [_textView selectedRange]; + DEBUG_LOG(@"Selected Range(TotalLen:%d): Loc:%d Len:%d", self.buffer.length, r.location, r.length); + [self _moveCursor:r.location preserveColumn:NO]; + _selectionBegin = _insertionPoint; + self.selectionMode = XVIM_VISUAL_NONE; +} + +/** + * Applies internal state to underlying view (self). + * This update self's property and applies the visual effect on it. + * All the state need to express Vim is held by this class and + * we use self to express it visually. + **/ +- (void)_syncState +{ + DEBUG_LOG(@"IP:%d", _insertionPoint); + + _syncStateLock = YES; + + if (_cursorMode == CURSOR_MODE_COMMAND) { + if (![self.buffer isNormalCursorPositionValidAtIndex:_insertionPoint]) { + NSRange placeholder = [(DVTSourceTextView *)_textView rangeOfPlaceholderFromCharacterIndex:_insertionPoint forward:NO wrap:NO limit:0]; + if (placeholder.location != NSNotFound && _insertionPoint == (placeholder.location + placeholder.length)) { + // The condition here means that just before current insertion point is a placeholder. + // So we select the the place holder and its already selected by "selectedPreviousPlaceholder" above + [self _moveCursor:placeholder.location preserveColumn:YES]; + } else { + [self _moveCursor:_insertionPoint - 1 preserveColumn:YES]; + } + } + } + + TRACE_LOG(@"mode:%d length:%d cursor:%d ip:%d begin:%d line:%d column:%d preservedColumn:%d", \ + _selectionMode, self.buffer.length, _cursorMode, _insertionPoint, _selectionBegin, + self.insertionLine, self.insertionColumn, _preservedColumn); + +#ifndef __XCODE5__ + [(DVTFoldingTextStorage*)_textView.textStorage increaseUsingFoldedRanges]; +#endif + [_textView xvim_setSelectedRanges:[self _selectedRanges] affinity:NSSelectionAffinityDownstream stillSelecting:NO]; +#ifndef __XCODE5__ + [(DVTFoldingTextStorage*)_textView.textStorage decreaseUsingFoldedRanges]; +#endif + + [self scrollTo:_insertionPoint]; + _syncStateLock = NO; +} + +#pragma mark *** Properties *** + +- (XVimBuffer *)buffer +{ + return _textView.textStorage.xvim_buffer; +} + +- (XVimPosition)insertionPosition +{ + return [self.buffer positionOfIndex:_insertionPoint]; +} + +- (NSUInteger)insertionColumn +{ + return [self.buffer columnOfIndex:_insertionPoint]; +} + +- (NSUInteger)insertionLine +{ + return [self.buffer lineNumberAtIndex:_insertionPoint]; +} + +- (XVimPosition)selectionPosition +{ + return [self.buffer positionOfIndex:_selectionBegin]; +} + +- (NSUInteger)selectionColumn +{ + return [self.buffer columnOfIndex:_selectionBegin]; +} + +- (NSUInteger)selectionLine +{ + return [self.buffer lineNumberAtIndex:_selectionBegin]; +} + +- (BOOL)inVisualMode +{ + return _selectionMode != XVIM_VISUAL_NONE; +} + +- (BOOL)inBlockMode +{ + return _selectionMode == XVIM_VISUAL_BLOCK; +} + +- (void)setSelectionMode:(XVimVisualMode)mode +{ + if (_selectionMode != mode) { + if (mode == XVIM_VISUAL_NONE) { + _selectionBegin = NSNotFound; + } else if (_selectionMode == XVIM_VISUAL_NONE) { + _selectionBegin = _insertionPoint; + } + _selectionMode = mode; + [self _syncState]; + } +} + + +#pragma mark *** Visual Mode and Cursor Position *** + +- (XVimRange)_selectedLines +{ + if (_selectionMode == XVIM_VISUAL_NONE) { // its not in selecting mode + return (XVimRange){ NSNotFound, NSNotFound }; + } else { + NSUInteger l1 = self.insertionLine; + NSUInteger l2 = self.selectionLine; + + return (XVimRange){ MIN(l1, l2), MAX(l1, l2) }; + } +} + +- (NSRange)_selectedRange +{ + XVimBuffer *buffer = self.buffer; + + if (_selectionMode == XVIM_VISUAL_NONE) { + return NSMakeRange(_insertionPoint, 0); + } + + if (_selectionMode == XVIM_VISUAL_CHARACTER) { + XVimRange xvr = XVimMakeRange(_selectionBegin, _insertionPoint); + + if (xvr.begin > xvr.end) { + xvr = XVimRangeSwap(xvr); + } + if (xvr.end >= buffer.length) { + xvr.end--; + } + return XVimMakeNSRange(xvr); + } + + if (_selectionMode == XVIM_VISUAL_LINE) { + XVimRange lines = [self _selectedLines]; + NSUInteger begin = [buffer indexOfLineNumber:lines.begin]; + NSUInteger end = [buffer indexOfLineNumber:lines.end]; + + end = [buffer endOfLine:end]; + if (end >= buffer.length) { + end--; + } + return NSMakeRange(begin, end - begin + 1); + } + + return NSMakeRange(NSNotFound, 0); +} + +- (XVimSelection)_selectedBlock +{ + XVimSelection result = { }; + + if (_selectionMode == XVIM_VISUAL_NONE) { + result.top = result.bottom = result.left = result.right = NSNotFound; + return result; + } + + XVimBuffer *buffer = self.buffer; + NSUInteger l1, c11, c12; + NSUInteger l2, c21, c22; + NSUInteger tabWidth = buffer.tabWidth; + NSUInteger pos; + + pos = _selectionBegin; + l1 = [buffer lineNumberAtIndex:pos]; + c11 = [buffer columnOfIndex:pos]; + if (!tabWidth || pos >= buffer.length || [buffer.string characterAtIndex:pos] != '\t') { + c12 = c11; + } else { + c12 = c11 + tabWidth - (c11 % tabWidth) - 1; + } + + pos = _insertionPoint; + l2 = [buffer lineNumberAtIndex:pos]; + c21 = [buffer columnOfIndex:pos]; + if (!tabWidth || pos >= buffer.length || [buffer.string characterAtIndex:pos] != '\t') { + c22 = c21; + } else { + c22 = c21 + tabWidth - (c21 % tabWidth) - 1; + } + + if (l1 <= l2) { + result.corner |= _XVIM_VISUAL_BOTTOM; + } + if (c11 <= c22) { + result.corner |= _XVIM_VISUAL_RIGHT; + } + result.top = MIN(l1, l2); + result.bottom = MAX(l1, l2); + result.left = MIN(c11, c21); + result.right = MAX(c12, c22); + if (_preservedColumn == XVimSelectionEOL) { + result.right = XVimSelectionEOL; + } + return result; +} + +- (NSArray *)_selectedRanges +{ + XVimBuffer *buffer = self.buffer; + + if (_selectionMode != XVIM_VISUAL_BLOCK) { + return [NSArray arrayWithObject:[NSValue valueWithRange:[self _selectedRange]]]; + } + + NSMutableArray *rangeArray = [[[NSMutableArray alloc] init] autorelease]; + XVimSelection sel = [self _selectedBlock]; + NSUInteger length = buffer.length; + + for (NSUInteger line = sel.top; line <= sel.bottom; line++) { + NSUInteger begin = [buffer indexOfLineNumber:line column:sel.left]; + NSUInteger end = [buffer indexOfLineNumber:line column:sel.right]; + + if (begin >= length) { + continue; + } + if (end >= length) { + end--; + } else if (sel.right != XVimSelectionEOL && [buffer isIndexAtEndOfLine:end]) { + end--; + } + [rangeArray addObject:[NSValue valueWithRange:NSMakeRange(begin, end - begin + 1)]]; + } + return rangeArray; +} + +- (void)selectSwapCorners:(BOOL)onSameLine +{ + if (_selectionMode == XVIM_VISUAL_BLOCK) { + XVimBuffer *buffer = self.buffer; + XVimPosition start, end; + XVimSelection sel; + NSUInteger pos; + + sel = [self _selectedBlock]; + if (onSameLine) { + sel.corner ^= _XVIM_VISUAL_RIGHT; + } else { + sel.corner ^= _XVIM_VISUAL_RIGHT | _XVIM_VISUAL_BOTTOM; + } + + if (sel.corner & _XVIM_VISUAL_BOTTOM) { + start.line = sel.top; + end.line = sel.bottom; + } else { + end.line = sel.top; + start.line = sel.bottom; + } + + if (sel.corner & _XVIM_VISUAL_RIGHT) { + start.column = sel.left; + end.column = sel.right; + } else { + end.column = sel.left; + start.column = sel.right; + } + + _selectionBegin = [buffer indexOfLineNumber:start.line column:start.column]; + pos = [buffer indexOfLineNumber:end.line column:end.column]; + [self _moveCursor:pos preserveColumn:NO]; + } else if (_selectionMode != XVIM_VISUAL_NONE) { + NSUInteger begin = _selectionBegin; + + _selectionBegin = _insertionPoint; + [self _moveCursor:begin preserveColumn:NO]; + } + [_textView setNeedsDisplay:YES]; + [self _syncState]; +} + +- (void)escapeFromInsertAndMoveBack:(BOOL)moveBack +{ + if (_cursorMode == CURSOR_MODE_INSERT) { + _cursorMode = CURSOR_MODE_COMMAND; + if (moveBack && ![self.buffer isIndexAtStartOfLine:_insertionPoint]) { + [self _moveCursor:_insertionPoint - 1 preserveColumn:NO]; + } + [self _syncState]; + } +} + +- (void)saveVisualInfoForBuffer:(XVimBuffer *)buffer +{ + XVimVisualInfo *vi = &buffer->visualInfo; + + vi->mode = _selectionMode; + vi->end = self.insertionPosition; + vi->start = self.selectionPosition; + vi->colwant = _preservedColumn; +} + +- (void)selectNextPlaceholder +{ +#ifdef __USE_DVTKIT__ + if ([_textView isKindOfClass:[DVTSourceTextView class]]) { + [(DVTSourceTextView *)_textView selectNextPlaceholder:self]; + } +#endif +} + +- (void)selectPreviousPlaceholder { +#ifdef __USE_DVTKIT__ + if ([_textView isKindOfClass:[DVTSourceTextView class]]) { + [(DVTSourceTextView *)_textView selectPreviousPlaceholder:self]; + } +#endif +} + +/** + * Adjust cursor position if the position is not valid as normal mode cursor position + * This method may changes selected range of the view. + */ +- (void)adjustCursorPosition +{ + NSRange range = _textView.selectedRange; + + // If the current cursor position is not valid for normal mode move it. + if (![self.buffer isNormalCursorPositionValidAtIndex:range.location]) { + [self selectPreviousPlaceholder]; + NSRange prevPlaceHolder = _textView.selectedRange; + + if (range.location != prevPlaceHolder.location && range.location == NSMaxRange(prevPlaceHolder)) { + // The condition here means that just before current insertion point is a placeholder. + // So we select the the place holder and its already selected by "selectedPreviousPlaceholder" above + } else { + _textView.selectedRange = NSMakeRange(range.location - 1, 0); + } + } + return; +} + +- (void)moveCursorToIndex:(NSUInteger)index +{ + [self _moveCursor:index preserveColumn:NO]; + [self _syncState]; +} + +- (void)moveCursorToPosition:(XVimPosition)pos +{ + NSUInteger index = [self.buffer indexOfLineNumber:pos.line column:pos.column]; + + [self _moveCursor:index preserveColumn:NO]; + if (pos.column == XVimSelectionEOL) { + _preservedColumn = XVimSelectionEOL; + } + [self _syncState]; +} + +- (NSUInteger)_fixupMotionEnd:(NSUInteger)end buffer:(XVimBuffer *)buffer motion:(XVimMotion *)motion +{ + NSUInteger search = end; + NSRange r; + + if (end < buffer.length && [buffer.string characterAtIndex:end] == '#') { + search++; + } + + r = [(DVTSourceTextView *)_textView rangeOfPlaceholderFromCharacterIndex:search forward:NO wrap:NO limit:0]; + if (r.location != NSNotFound && end < NSMaxRange(r)) { + if (motion.motion == MOTION_FORWARD) { + end = NSMaxRange(r); + } else { + end = r.location; + } + } + + if (![buffer isNormalCursorPositionValidAtIndex:end]) { + motion.info->reachedEndOfLine = YES; + end--; + } + return end; +} + +- (XVimRange)_getMotionRange:(NSUInteger)current motion:(XVimMotion*)motion +{ + NSRange range = NSMakeRange(NSNotFound, 0); + NSUInteger begin = current; + NSUInteger end = NSNotFound; + NSUInteger tmpPos = NSNotFound; + NSUInteger start = NSNotFound; + XVimBuffer *buffer = self.buffer; + NSTextStorage *ts = buffer.textStorage; + + switch (motion.motion) { + case MOTION_NONE: + // Do nothing + break; + case MOTION_FORWARD: + end = [buffer indexOfCharMotion:motion.scount index:begin options:motion.option]; + end = [self _fixupMotionEnd:end buffer:buffer motion:motion]; + break; + case MOTION_BACKWARD: + end = [buffer indexOfCharMotion:-motion.scount index:begin options:motion.option]; + end = [self _fixupMotionEnd:end buffer:buffer motion:motion]; + break; + case MOTION_WORD_FORWARD: + end = [ts wordsForward:begin count:motion.count option:motion.option info:motion.info]; + break; + case MOTION_WORD_BACKWARD: + end = [ts wordsBackward:begin count:motion.count option:motion.option]; + break; + case MOTION_END_OF_WORD_FORWARD: + end = [ts endOfWordsForward:begin count:motion.count option:motion.option]; + break; + case MOTION_END_OF_WORD_BACKWARD: + end = [ts endOfWordsBackward:begin count:motion.count option:motion.option]; + break; + case MOTION_LINE_FORWARD: + end = [buffer indexOfLineMotion:motion.scount index:begin column:_preservedColumn]; + break; + case MOTION_LINE_BACKWARD: + end = [buffer indexOfLineMotion:-motion.scount index:begin column:_preservedColumn]; + break; + case MOTION_BEGINNING_OF_LINE: + end = [buffer startOfLine:begin]; + break; + case MOTION_COLUMN_OF_LINE: + end = [buffer indexOfLineAtIndex:begin column:motion.count - 1]; + if (![buffer isNormalCursorPositionValidAtIndex:end]) { + motion.info->reachedEndOfLine = YES; + end--; + } + break; + case MOTION_END_OF_LINE: + end = [buffer indexOfLineMotion:motion.scount - 1 index:begin column:XVimSelectionEOL]; + break; + case MOTION_SENTENCE_FORWARD: + end = [ts sentencesForward:begin count:motion.count option:motion.option]; + break; + case MOTION_SENTENCE_BACKWARD: + end = [ts sentencesBackward:begin count:motion.count option:motion.option]; + break; + case MOTION_PARAGRAPH_FORWARD: + end = [ts moveFromIndex:begin paragraphs:motion.scount option:motion.option]; + break; + case MOTION_PARAGRAPH_BACKWARD: + end = [ts moveFromIndex:begin paragraphs:-motion.scount option:motion.option]; + break; + case MOTION_NEXT_CHARACTER: + end = [ts nextCharacterInLine:begin count:motion.count character:motion.character option:MOPT_NONE]; + break; + case MOTION_PREV_CHARACTER: + end = [ts prevCharacterInLine:begin count:motion.count character:motion.character option:MOPT_NONE]; + break; + case MOTION_TILL_NEXT_CHARACTER: + end = [ts nextCharacterInLine:begin count:motion.count character:motion.character option:MOPT_NONE]; + if (end != NSNotFound) { + end--; + } + break; + case MOTION_TILL_PREV_CHARACTER: + end = [ts prevCharacterInLine:begin count:motion.count character:motion.character option:MOPT_NONE]; + if (end != NSNotFound) { + end++; + } + break; + case MOTION_NEXT_FIRST_NONBLANK: + end = [buffer indexOfLineMotion:motion.scount index:begin column:0]; + end = [buffer firstNonblankInLineAtIndex:end allowEOL:YES]; + break; + case MOTION_PREV_FIRST_NONBLANK: + end = [buffer indexOfLineMotion:-motion.scount index:begin column:0 ]; + end = [buffer firstNonblankInLineAtIndex:end allowEOL:YES]; + break; + case MOTION_FIRST_NONBLANK: + end = [buffer firstNonblankInLineAtIndex:begin allowEOL:NO]; + break; + case MOTION_LINENUMBER: + end = [buffer indexOfLineNumber:motion.line column:_preservedColumn]; + if (NSNotFound == end) { + end = [buffer indexOfLineMotion:0 index:buffer.length column:_preservedColumn]; + } + break; + case MOTION_PERCENT: + end = [buffer indexOfLineNumber:1 + ([buffer numberOfLines]-1) * motion.count/100]; + break; + case MOTION_NEXT_MATCHED_ITEM: + end = [ts positionOfMatchedPair:begin]; + break; + case MOTION_LASTLINE: + end = [buffer indexOfLineMotion:0 index:buffer.length column:_preservedColumn]; + break; + case MOTION_HOME: + tmpPos = [self lineNumberInScrollView:0.0 offset:motion.scount - 1]; + end = [buffer firstNonblankInLineAtIndex:[buffer indexOfLineNumber:tmpPos] allowEOL:YES]; + break; + case MOTION_MIDDLE: + tmpPos = [self lineNumberInScrollView:0.5 offset:0]; + end = [buffer firstNonblankInLineAtIndex:[buffer indexOfLineNumber:tmpPos] allowEOL:YES]; + break; + case MOTION_BOTTOM: + tmpPos = [self lineNumberInScrollView:1.0 offset:1 - motion.scount]; + end = [buffer firstNonblankInLineAtIndex:[buffer indexOfLineNumber:tmpPos] allowEOL:YES]; + break; + case MOTION_SEARCH_FORWARD: + end = [ts searchRegexForward:motion.regex from:_insertionPoint count:motion.count option:motion.option].location; + break; + case MOTION_SEARCH_BACKWARD: + end = [ts searchRegexBackward:motion.regex from:_insertionPoint count:motion.count option:motion.option].location; + break; + case TEXTOBJECT_WORD: + range = [ts currentWord:begin count:motion.count option:motion.option]; + break; + case TEXTOBJECT_BRACES: + range = xv_current_block(buffer.string, current, motion.count, !(motion.option & MOPT_TEXTOBJECT_INNER), '{', '}'); + break; + case TEXTOBJECT_PARAGRAPH: + // Not supported + start = [ts moveFromIndex:_insertionPoint paragraphs:-1 option:MOPT_PARA_BOUND_BLANKLINE]; + end = [ts moveFromIndex:_insertionPoint paragraphs:motion.scount option:MOPT_PARA_BOUND_BLANKLINE]; + range = NSMakeRange(start, end - start); + break; + case TEXTOBJECT_PARENTHESES: + range = xv_current_block(buffer.string, current, motion.count, !(motion.option & MOPT_TEXTOBJECT_INNER), '(', ')'); + break; + case TEXTOBJECT_SENTENCE: + // Not supported + break; + case TEXTOBJECT_ANGLEBRACKETS: + range = xv_current_block(buffer.string, current, motion.count, !(motion.option & MOPT_TEXTOBJECT_INNER), '<', '>'); + break; + case TEXTOBJECT_SQUOTE: + range = xv_current_quote(buffer.string, current, motion.count, !(motion.option & MOPT_TEXTOBJECT_INNER), '\''); + break; + case TEXTOBJECT_DQUOTE: + range = xv_current_quote(buffer.string, current, motion.count, !(motion.option & MOPT_TEXTOBJECT_INNER), '\"'); + break; + case TEXTOBJECT_TAG: + // Not supported + break; + case TEXTOBJECT_BACKQUOTE: + range = xv_current_quote(buffer.string, current, motion.count, !(motion.option & MOPT_TEXTOBJECT_INNER), '`'); + break; + case TEXTOBJECT_SQUAREBRACKETS: + range = xv_current_block(buffer.string, current, motion.count, !(motion.option & MOPT_TEXTOBJECT_INNER), '[', ']'); + break; + case MOTION_LINE_COLUMN: + end = [buffer indexOfLineNumber:motion.line column:motion.column]; + if (NSNotFound == end) { + end = current; + } + break; + case MOTION_POSITION: + end = motion.position; + break; + } + + if (range.location != NSNotFound) {// This block is for TEXTOBJECT + begin = range.location; + if (range.length == 0) { + end = NSNotFound; + }else{ + end = range.location + range.length - 1; + } + } + + TRACE_LOG(@"range location:%u length:%u", begin, end); + return XVimMakeRange(begin, end); +} + +- (void)moveCursorWithMotion:(XVimMotion*)motion +{ + XVimRange r = [self _getMotionRange:_insertionPoint motion:motion]; + + if (r.end == NSNotFound) { + return; + } + + if (_selectionMode != XVIM_VISUAL_NONE && [motion isTextObject]) { + if( _selectionMode == XVIM_VISUAL_LINE) { + // Motion with text object in VISUAL LINE changes visual mode to VISUAL CHARACTER + self.selectionMode = XVIM_VISUAL_CHARACTER; + } + + if (_insertionPoint < _selectionBegin) { + // When insertionPoint < selectionBegin it only changes insertion point to begining of the text object + [self _moveCursor:r.begin preserveColumn:NO]; + } else { + // Text object expands one text object ( the text object under insertion point + 1 ) + if (_insertionPoint + 1 < self.buffer.length) { + r = [self _getMotionRange:_insertionPoint + 1 motion:motion]; + } + if (_selectionBegin > r.begin) { + _selectionBegin = r.begin; + } + [self _moveCursor:r.end preserveColumn:NO]; + } + } else { + BOOL preserveColumn = YES; + + switch (motion.motion) { + case MOTION_COLUMN_OF_LINE: + _preservedColumn = motion.count - 1; + break; + case MOTION_END_OF_LINE: + _preservedColumn = XVimSelectionEOL; + break; + case MOTION_LINE_BACKWARD: + case MOTION_LINE_FORWARD: + case MOTION_LASTLINE: + case MOTION_LINENUMBER: + break; + default: + preserveColumn = NO; + break; + } + [self _moveCursor:r.end preserveColumn:preserveColumn]; + } + [_textView setNeedsDisplay:YES]; + [self _syncState]; +} + +#pragma mark *** Operations *** + +- (NSRange)_getOperationRange:(XVimRange)xrange type:(MOTION_TYPE)type +{ + XVimBuffer *buffer = self.buffer; + NSUInteger length = buffer.length; + + if (buffer.length == 0) { + return NSMakeRange(0, 0); + } + + if (xrange.begin > xrange.end) { + xrange = XVimRangeSwap(xrange); + } + + // EOF can not be included in operation range. + if (xrange.begin >= length) { + return NSMakeRange(length, 0); + } + + // EOF should not be included. + // If type is exclusive we do not subtract 1 because we do it later below + if (xrange.end >= length && type != CHARACTERWISE_EXCLUSIVE) { + // Note that we already know that "to" is not 0 so not chekcing if its 0. + xrange.end--; + } + + // At this point "from" and "to" is not EOF + if (type == CHARACTERWISE_EXCLUSIVE) { + // to will not be included. + xrange.end--; + } else if (type == CHARACTERWISE_INCLUSIVE) { + // Nothing special + } else if (type == LINEWISE) { + xrange.end = [buffer endOfLine:xrange.end]; + if (xrange.end >= length) { + xrange.end--; + } + xrange.begin = [buffer startOfLine:xrange.begin]; + } + + return XVimMakeNSRange(xrange); +} + +- (void)_registerInsertionPointForUndo +{ + XVimUndoOperation *op = [[XVimUndoOperation alloc] initWithIndex:_insertionPoint]; + [op registerForBuffer:self.buffer]; + [op release]; +} + +- (void)__startYankWithType:(MOTION_TYPE)type +{ + if (_selectionMode == XVIM_VISUAL_NONE) { + if (type == CHARACTERWISE_EXCLUSIVE || type == CHARACTERWISE_INCLUSIVE) { + _lastYankedType = TEXT_TYPE_CHARACTERS; + } else if (type == LINEWISE) { + _lastYankedType = TEXT_TYPE_LINES; + } + } else if (_selectionMode == XVIM_VISUAL_CHARACTER) { + _lastYankedType = TEXT_TYPE_CHARACTERS; + } else if (_selectionMode == XVIM_VISUAL_LINE) { + _lastYankedType = TEXT_TYPE_LINES; + } else if (_selectionMode == XVIM_VISUAL_BLOCK) { + _lastYankedType = TEXT_TYPE_BLOCK; + } + TRACE_LOG(@"YANKED START WITH TYPE:%d", _lastYankedType); +} + +- (void)_yankRange:(NSRange)range withType:(MOTION_TYPE)type +{ + XVimBuffer *buffer = self.buffer; + NSString *string = buffer.string; + NSString *s; + BOOL needsNL; + + [self __startYankWithType:type]; + + needsNL = _lastYankedType == TEXT_TYPE_LINES; + if (range.length) { + s = [string substringWithRange:range]; + if (needsNL && !isNewline([s characterAtIndex:s.length - 1])) { + s = [s stringByAppendingString:buffer.lineEnding]; + } + } else if (needsNL) { + s = buffer.lineEnding; + } else { + s = @""; + } + + [_lastYankedText release]; + _lastYankedText = [s retain]; + TRACE_LOG(@"YANKED STRING : %@", s); +} + +- (void)_yankSelection:(XVimSelection)sel +{ + XVimBuffer *buffer = self.buffer; + NSString *string = buffer.string; + NSUInteger tabWidth = buffer.tabWidth; + + NSMutableString *ybuf = [[NSMutableString alloc] init]; + + _lastYankedType = TEXT_TYPE_BLOCK; + + for (NSUInteger line = sel.top; line <= sel.bottom; line++) { + NSUInteger lpos = [buffer indexOfLineNumber:line column:sel.left]; + NSUInteger rpos = [buffer indexOfLineNumber:line column:sel.right]; + + /* if lpos points in the middle of a tab, split it and advance lpos */ + if (lpos < string.length && [string characterAtIndex:lpos] == '\t') { + NSUInteger lcol = sel.left - (sel.left % tabWidth); + + if (lcol < sel.left) { + NSUInteger count = tabWidth - (sel.left - lcol); + + if (lpos == rpos) { + /* if rpos points to the same tab, truncate it to the right also */ + count = sel.right - sel.left + 1; + } + CFStringPad((CFMutableStringRef)ybuf, CFSTR(" "), + (CFIndex)(ybuf.length + count), 0); + lpos++; + } + } + + if (lpos <= rpos) { + if (sel.right == XVimSelectionEOL) { + [ybuf appendString:[string substringWithRange:NSMakeRange(lpos, rpos - lpos)]]; + } else { + NSRange r = NSMakeRange(lpos, rpos - lpos + 1); + NSUInteger rcol; + BOOL mustPad = NO; + + if (rpos >= string.length) { + rcol = [buffer columnOfIndex:rpos]; + mustPad = YES; + r.length--; + } else { + unichar c = [string characterAtIndex:rpos]; + if (isNewline(c)) { + rcol = [buffer columnOfIndex:rpos]; + mustPad = YES; + r.length--; + } else if (c == '\t') { + rcol = [buffer columnOfIndex:rpos]; + if (sel.right - rcol + 1 < tabWidth) { + mustPad = YES; + r.length--; + } + } + } + + if (r.length) { + [ybuf appendString:[string substringWithRange:r]]; + } + + if (mustPad) { + [ybuf appendString:[NSString stringMadeOfSpaces:sel.right - rcol + 1]]; + } + } + } + [ybuf appendString:buffer.lineEnding]; + } + + [_lastYankedText release]; + _lastYankedText = ybuf; + TRACE_LOG(@"YANKED STRING : %@", ybuf); +} + +- (void)_killSelection:(XVimSelection)sel +{ + XVimBuffer *buffer = self.buffer; + NSString *string = buffer.string; + NSUInteger tabWidth = buffer.tabWidth; + + for (NSUInteger line = sel.bottom; line >= sel.top; line--) { + NSUInteger lpos = [buffer indexOfLineNumber:line column:sel.left]; + NSUInteger rpos = [buffer indexOfLineNumber:line column:sel.right]; + NSUInteger nspaces = 0; + + if (lpos >= string.length) { + continue; + } + + if ([string characterAtIndex:lpos] == '\t') { + NSUInteger lcol = [buffer columnOfIndex:lpos]; + + if (lcol < sel.left) { + nspaces = sel.left - lcol; + if (lpos == rpos) { + nspaces = tabWidth - (sel.right - sel.left + 1); + } + } + } + + if ([buffer isIndexAtEndOfLine:rpos]) { + rpos--; + } else if (lpos < rpos) { + if ([string characterAtIndex:rpos] == '\t') { + nspaces += tabWidth - (sel.right - [buffer columnOfIndex:rpos] + 1); + } + } + + NSRange range = NSMakeRange(lpos, rpos - lpos + 1); + + [buffer replaceCharactersInRange:range withSpaces:nspaces]; + } +} + +- (void)doDelete:(XVimMotion *)motion andYank:(BOOL)yank +{ + XVimBuffer *buffer = self.buffer; + + NSAssert(!(_selectionMode == XVIM_VISUAL_NONE && motion == nil), + @"motion must be specified if current selection mode is not visual"); + if (_insertionPoint == 0 && buffer.length == 0) { + return ; + } + NSUInteger pos = _insertionPoint; + + motion.info->deleteLastLine = NO; + if (_selectionMode == XVIM_VISUAL_NONE) { + XVimRange motionRange = [self _getMotionRange:pos motion:motion]; + NSRange r; + + if (motionRange.end == NSNotFound) { + return; + } + + // We have to treat some special cases + // When a cursor get end of line with "l" motion, make the motion type to inclusive. + // This make you to delete the last character. (if its exclusive last character never deleted with "dl") + if (motion.motion == MOTION_FORWARD && motion.info->reachedEndOfLine) { + if (motion.type == CHARACTERWISE_EXCLUSIVE) { + motion.type = CHARACTERWISE_INCLUSIVE; + } else if (motion.type == CHARACTERWISE_INCLUSIVE) { + motion.type = CHARACTERWISE_EXCLUSIVE; + } + } + if (motion.motion == MOTION_WORD_FORWARD) { + if ((motion.info->isFirstWordInLine && motion.info->lastEndOfLine != NSNotFound)) { + // Special cases for word move over a line break. + motionRange.end = motion.info->lastEndOfLine; + motion.type = CHARACTERWISE_INCLUSIVE; + } + else if (motion.info->reachedEndOfLine) { + if (motion.type == CHARACTERWISE_EXCLUSIVE) { + motion.type = CHARACTERWISE_INCLUSIVE; + } else if (motion.type == CHARACTERWISE_INCLUSIVE) { + motion.type = CHARACTERWISE_EXCLUSIVE; + } + } + } + r = [self _getOperationRange:motionRange type:motion.type]; + + if (motion.type == LINEWISE && [buffer isIndexOnLastLine:motionRange.end]) { + // eat the previous end of line as well + if (r.location > 0) { + NSUInteger endOfPreviousLine = [buffer endOfLine:r.location - 1]; + + r.length += r.location - endOfPreviousLine; + r.location = endOfPreviousLine; + motion.info->deleteLastLine = YES; + } + } + if (yank) { + [self _yankRange:r withType:motion.type]; + } + + pos = r.location; + [self _moveCursor:pos preserveColumn:NO]; + [buffer beginEditingAtIndex:pos]; + [buffer replaceCharactersInRange:r withString:@""]; + if (motion.type == LINEWISE) { + pos = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; + } + } else if (self.selectionMode != XVIM_VISUAL_BLOCK) { + BOOL toFirstNonBlank = (self.selectionMode == XVIM_VISUAL_LINE); + NSRange range = [self _selectedRange]; + + // Currently not supportin deleting EOF with selection mode. + // This is because of the fact that NSTextView does not allow select EOF + + if (yank) { + [self _yankRange:range withType:DEFAULT_MOTION_TYPE]; + } + + pos = range.location; + [self _moveCursor:pos preserveColumn:NO]; + [buffer beginEditingAtIndex:pos]; + [buffer replaceCharactersInRange:range withString:@""]; + if (toFirstNonBlank) { + pos = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; + } + } else { + XVimSelection sel = [self _selectedBlock]; + if (yank) { + [self _yankSelection:sel]; + } + pos = [buffer indexOfLineNumber:sel.top column:sel.left]; + [buffer beginEditingAtIndex:pos]; + [self _killSelection:sel]; + } + [buffer endEditingAtIndex:pos]; + + [_delegate textView:_textView didDelete:_lastYankedText withType:_lastYankedType]; + + [self _moveCursor:pos preserveColumn:NO]; + self.selectionMode = XVIM_VISUAL_NONE; + [self _syncState]; +} + +- (void)doChange:(XVimMotion *)motion +{ + XVimBuffer *buffer = self.buffer; + + BOOL insertNewline = NO; + if (motion.type == LINEWISE || _selectionMode == XVIM_VISUAL_LINE) { + // 'cc' deletes the lines but need to keep the last newline. + // So insertNewline as 'O' does before entering insert mode + insertNewline = YES; + } + + // "cw" is like "ce" if the cursor is on a word ( in this case blank line is not treated as a word ) + if (motion.motion == MOTION_WORD_FORWARD && [_textView.textStorage isNonblank:_insertionPoint]) { + motion.motion = MOTION_END_OF_WORD_FORWARD; + motion.type = CHARACTERWISE_INCLUSIVE; + motion.option |= MOPT_CHANGE_WORD; + } + + [buffer beginEditingAtIndex:_insertionPoint]; + _cursorMode = CURSOR_MODE_INSERT; + [self doDelete:motion andYank:YES]; + + if (motion.info->deleteLastLine || insertNewline) { + [self _insertNewlineAboveLine:[buffer lineNumberAtIndex:_insertionPoint]]; + } + + [buffer endEditingAtIndex:_insertionPoint]; + + self.selectionMode = XVIM_VISUAL_NONE; + [self _syncState]; +} + +- (void)doYank:(XVimMotion*)motion +{ + XVimBuffer *buffer = self.buffer; + + NSAssert( !(_selectionMode == XVIM_VISUAL_NONE && motion == nil), + @"motion must be specified if current selection mode is not visual"); + NSUInteger newPos = NSNotFound; + + if (_selectionMode == XVIM_VISUAL_NONE) { + XVimRange to = [self _getMotionRange:_insertionPoint motion:motion]; + NSRange r; + + if (NSNotFound == to.end) { + return; + } + + // We have to treat some special cases (same as delete) + if (motion.motion == MOTION_FORWARD && motion.info->reachedEndOfLine) { + motion.type = CHARACTERWISE_INCLUSIVE; + } + if (motion.motion == MOTION_WORD_FORWARD) { + if ((motion.info->isFirstWordInLine && motion.info->lastEndOfLine != NSNotFound)) { + // Special cases for word move over a line break. + to.end = motion.info->lastEndOfLine; + motion.type = CHARACTERWISE_INCLUSIVE; + } + else if (motion.info->reachedEndOfLine) { + if (motion.type == CHARACTERWISE_EXCLUSIVE) { + motion.type = CHARACTERWISE_INCLUSIVE; + } else if (motion.type == CHARACTERWISE_INCLUSIVE) { + motion.type = CHARACTERWISE_EXCLUSIVE; + } + } + } + r = [self _getOperationRange:to type:motion.type]; + if (motion.type == LINEWISE && to.end >= buffer.length && [buffer isIndexAtStartOfLine:to.end]) { + if (r.location > 0) { + NSUInteger endOfPreviousLine = [buffer endOfLine:r.location - 1]; + + r.length += r.location - endOfPreviousLine; + r.location = endOfPreviousLine; + } + } + [self _yankRange:r withType:motion.type]; + } else if (self.selectionMode != XVIM_VISUAL_BLOCK) { + NSRange range = [self _selectedRange]; + + newPos = range.location; + [self _yankRange:range withType:DEFAULT_MOTION_TYPE]; + } else { + XVimSelection sel = [self _selectedBlock]; + + newPos = [buffer indexOfLineNumber:sel.top column:sel.left]; + [self _yankSelection:sel]; + } + + [_delegate textView:_textView didYank:_lastYankedText withType:_lastYankedType]; + if (newPos != NSNotFound) { + [self _moveCursor:newPos preserveColumn:NO]; + } + self.selectionMode = XVIM_VISUAL_NONE; + [self _syncState]; +} + +- (void)doPut:(NSString *)_text withType:(TEXT_TYPE)type afterCursor:(bool)after count:(NSUInteger)count +{ + XVimBuffer *buffer = self.buffer; + NSMutableString *text = [_text mutableCopy]; + + TRACE_LOG(@"text:%@ type:%d afterCursor:%d count:%d", text, type, after, count); + + [buffer beginEditingAtIndex:_insertionPoint]; + + if (self.selectionMode != XVIM_VISUAL_NONE) { + [self doDelete:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOPT_NONE, 1) andYank:YES]; + after = NO; + } + + NSUInteger insertionPointAfterPut = _insertionPoint; + NSUInteger targetPos = _insertionPoint; + + if (type == TEXT_TYPE_CHARACTERS) { + // Forward insertion point +1 if after flag if on + if (0 != text.length) { + if (![buffer isIndexAtEndOfLine:_insertionPoint] && after) { + targetPos++; + } + insertionPointAfterPut = targetPos; + for (NSUInteger i = 0; i < count ; i++) { + [buffer replaceCharactersInRange:NSMakeRange(targetPos, 0) withString:text]; + } + insertionPointAfterPut += text.length * count - 1; + } + } else if (type == TEXT_TYPE_LINES) { + if (after) { + [self _insertNewlineBelowCurrentLine]; + targetPos = _insertionPoint; + } else { + targetPos= [buffer startOfLine:_insertionPoint]; + } + insertionPointAfterPut = targetPos; + if (after) { + // delete newline at the end. (TEXT_TYPE_LINES always have newline at the end of the text) + [text replaceCharactersInRange:NSMakeRange(text.length - 1, 1) withString:@""]; + } + for (NSUInteger i = 0; i < count ; i++) { + [buffer replaceCharactersInRange:NSMakeRange(targetPos, 0) withString:text]; + targetPos += text.length; + } + } else if (type == TEXT_TYPE_BLOCK) { + // Forward insertion point +1 if after flag if on + if (![buffer isIndexAtEndOfLine:_insertionPoint] && after) { + _insertionPoint++; + } + insertionPointAfterPut = _insertionPoint; + + NSUInteger insertPos = _insertionPoint; + NSUInteger column = [buffer columnOfIndex:insertPos]; + NSUInteger startLine = [buffer lineNumberAtIndex:insertPos]; + NSArray *lines = [text componentsSeparatedByString:buffer.lineEnding]; + + for (NSUInteger i = 0 ; i < lines.count ; i++) { + NSString *line = [lines objectAtIndex:i]; + NSUInteger targetLine = startLine + i; + NSUInteger head = [buffer indexOfLineNumber:targetLine]; + + if (NSNotFound == head) { + NSAssert( targetLine != 0, @"This should not be happen"); + [buffer replaceCharactersInRange:NSMakeRange(buffer.length, 0) withString:buffer.lineEnding]; + head = buffer.length; + } + NSAssert(NSNotFound != head, @"Head of the target line must be found at this point"); + + // Find next insertion point + NSUInteger max = [buffer numberOfColumnsInLineAtIndex:head]; + + // FIXME: deal with tabs here + + // If the line does not have enough column pad it with spaces + if (column > max) { + NSUInteger end = [buffer endOfLine:head]; + + [buffer replaceCharactersInRange:NSMakeRange(end, 0) withSpaces:column - max]; + } + + NSUInteger pos = [buffer indexOfLineNumber:targetLine column:column]; + + for (NSUInteger i = 0; i < count ; i++) { + [buffer replaceCharactersInRange:NSMakeRange(pos, 0) withString:line]; + pos += line.length; + } + } + } + + [text release]; + [buffer endEditingAtIndex:insertionPointAfterPut]; + + [self _moveCursor:insertionPointAfterPut preserveColumn:NO]; + self.selectionMode = XVIM_VISUAL_NONE; + [self _syncState]; +} + +- (void)doSwapCharacters:(XVimMotion *)motion mode:(int)mode +{ + XVimBuffer *buffer = self.buffer; + NSUInteger undoPos = _insertionPoint; + NSUInteger endPos; + + if (buffer.length == 0) { + return; + } + + if (self.selectionMode == XVIM_VISUAL_NONE) { + NSRange range; + + if (motion.motion == MOTION_NONE) { + XVimMotion *m = XVIM_MAKE_MOTION(MOTION_FORWARD,CHARACTERWISE_EXCLUSIVE,MOPT_NOWRAP,motion.count); + XVimRange r = [self _getMotionRange:undoPos motion:m]; + + if (r.end == NSNotFound) { + return; + } + if (m.info->reachedEndOfLine) { + range = [self _getOperationRange:r type:CHARACTERWISE_INCLUSIVE]; + } else { + range = [self _getOperationRange:r type:CHARACTERWISE_EXCLUSIVE]; + } + endPos = r.end; + } else { + XVimRange to = [self _getMotionRange:undoPos motion:motion]; + if (to.end == NSNotFound) { + return; + } + + range = [self _getOperationRange:to type:motion.type]; + endPos = range.location; + } + + [buffer beginEditingAtIndex:undoPos]; + [buffer swapCharactersInRange:range mode:mode]; + [buffer endEditingAtIndex:endPos]; + } else { + NSArray *ranges = [self _selectedRanges]; + + endPos = undoPos = [[ranges objectAtIndex:0] rangeValue].location; + [buffer beginEditingAtIndex:undoPos]; + for (NSValue *v in ranges) { + [buffer swapCharactersInRange:v.rangeValue mode:mode]; + } + [buffer endEditingAtIndex:endPos]; + } + + [self _moveCursor:endPos preserveColumn:NO]; + self.selectionMode = XVIM_VISUAL_NONE; + [self _syncState]; +} + +- (BOOL)doReplaceCharacters:(unichar)c count:(NSUInteger)count +{ + XVimBuffer *buffer = self.buffer; + NSUInteger end = [buffer endOfLine:_insertionPoint]; + + // Note : endOfLine may return one less than _insertionPoint if _insertionPoint is on newline + if (NSNotFound == end) { + return NO; + } + + if (_insertionPoint + count > end) { + return NO; + } + + NSMutableString *s = [[NSMutableString alloc] initWithCapacity:count]; + unichar buf[8] = { c, c, c, c, c, c, c, c }; + NSUInteger pos = _insertionPoint; + + [buffer beginEditingAtIndex:pos]; + for (NSUInteger i = 0; i < count; i += 8) { + [s appendCharacters:buf length:MIN(8, count - i)]; + } + [buffer replaceCharactersInRange:NSMakeRange(pos, count) withString:s]; + [buffer endEditingAtIndex:pos]; + + [s release]; + + [self _moveCursor:pos + count preserveColumn:NO]; + [self _syncState]; + return YES; +} + +- (void)_joinAtLineNumber:(NSUInteger)line +{ + XVimBuffer *buffer = self.buffer; + NSUInteger headOfLine = [buffer indexOfLineNumber:line]; + NSTextStorage *ts = _textView.textStorage; + BOOL needSpace = NO; + + if (headOfLine == NSNotFound) { + return; + } + + NSUInteger tail = [buffer endOfLine:headOfLine]; + if (tail >= buffer.length) { + // This is the last line and nothing to join + return; + } + + // Check if we need to insert space between lines. + NSUInteger lastOfLine = [buffer lastOfLine:headOfLine]; + if (lastOfLine != NSNotFound) { + // This is not blank line so we check if the last character is space or not . + if (![ts isWhitespace:lastOfLine]) { + needSpace = YES; + } + } + + // Search in next line for the position to join(skip white spaces in next line) + NSUInteger posToJoin = [buffer indexOfLineMotion:1 index:headOfLine column:0]; + + posToJoin = [buffer nextNonblankInLineAtIndex:posToJoin allowEOL:YES]; + if (posToJoin < buffer.length && [buffer.string characterAtIndex:posToJoin] == ')') { + needSpace = NO; + } + + // delete "tail" to "posToJoin" excluding the position of "posToJoin" and insert space if need. + if (needSpace) { + [buffer replaceCharactersInRange:NSMakeRange(tail, posToJoin - tail) withString:@" "]; + } else { + [buffer replaceCharactersInRange:NSMakeRange(tail, posToJoin - tail) withString:@""]; + } + + // Move cursor + [self _moveCursor:tail preserveColumn:NO]; +} + +- (void)doJoin:(NSUInteger)count addSpace:(BOOL)addSpace +{ + XVimBuffer *buffer = self.buffer; + NSUInteger line; + + [buffer beginEditingAtIndex:_insertionPoint]; + + if (_selectionMode == XVIM_VISUAL_NONE) { + line = self.insertionLine; + } else { + XVimRange lines = [self _selectedLines]; + + line = lines.begin; + count = MAX(1, lines.end - lines.begin); + } + + if (addSpace) { + for (NSUInteger i = 0; i < count; i++) { + [self _joinAtLineNumber:line]; + } + } else { + NSUInteger pos = [buffer indexOfLineNumber:line]; + + for (NSUInteger i = 0; i < count; i++) { + NSUInteger tail = [buffer endOfLine:pos]; + + if (tail < buffer.length) { + [buffer replaceCharactersInRange:NSMakeRange(tail, 1) withString:@""]; + [self _moveCursor:tail preserveColumn:NO]; + } + } + } + + [buffer endEditingAtIndex:_insertionPoint]; + self.selectionMode = XVIM_VISUAL_NONE; + [self _syncState]; +} + +- (void)doFilter:(XVimMotion *)motion +{ + XVimBuffer *buffer = self.buffer; + + if (_insertionPoint == 0 && buffer.length == 0) { + return ; + } + + NSRange filterRange; + NSUInteger line, pos; + + if (self.selectionMode == XVIM_VISUAL_NONE) { + XVimRange to = [self _getMotionRange:_insertionPoint motion:motion]; + if (to.end == NSNotFound) { + return; + } + filterRange = [self _getOperationRange:to type:LINEWISE]; + line = [buffer lineNumberAtIndex:filterRange.location]; + } else { + XVimRange lines = [self _selectedLines]; + NSUInteger from = [buffer indexOfLineNumber:lines.begin]; + NSUInteger to = [buffer indexOfLineNumber:lines.end]; + + filterRange = [self _getOperationRange:XVimMakeRange(from, to) type:LINEWISE]; + line = lines.begin; + } + + [buffer indentCharacterRange:filterRange]; + + pos = [buffer indexOfLineNumber:line]; + pos = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; + + [self _moveCursor:pos preserveColumn:NO]; + self.selectionMode = XVIM_VISUAL_NONE; + [self _syncState]; +} + +- (void)doShift:(XVimMotion *)motion right:(BOOL)right +{ + XVimBuffer *buffer = self.buffer; + + if (_insertionPoint == 0 && buffer.length == 0) { + return ; + } + + NSUInteger shiftWidth = buffer.indentWidth; + NSUInteger column = 0, pos; + XVimRange lines; + BOOL blockMode = NO; + + if (self.selectionMode == XVIM_VISUAL_NONE) { + XVimRange to = [self _getMotionRange:_insertionPoint motion:motion]; + if (to.end == NSNotFound) { + return; + } + lines = XVimMakeRange([buffer lineNumberAtIndex:to.begin], [buffer lineNumberAtIndex:to.end]); + } else if (_selectionMode != XVIM_VISUAL_BLOCK) { + lines = [self _selectedLines]; + shiftWidth *= motion.count; + } else { + XVimSelection sel = [self _selectedBlock]; + + column = sel.left; + lines = XVimMakeRange(sel.top, sel.bottom); + shiftWidth *= motion.count; + blockMode = YES; + } + + if (blockMode) { + pos = [buffer indexOfLineNumber:lines.begin column:column]; + } else { + pos = [buffer indexOfLineNumber:lines.begin]; + pos = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; + } + + [buffer beginEditingAtIndex:pos]; + pos = [buffer shiftLines:lines column:column + count:shiftWidth right:right block:blockMode]; + [buffer endEditingAtIndex:pos]; + + [self _moveCursor:pos preserveColumn:NO]; + self.selectionMode = XVIM_VISUAL_NONE; + [self _syncState]; +} + +- (void)_insertNewlineBelowCurrentLine +{ + XVimBuffer *buffer = self.buffer; + + NSUInteger pos = [buffer startOfLine:_insertionPoint]; + + _insertionPoint = pos; + [buffer beginEditingAtIndex:_insertionPoint]; + pos = [buffer endOfLine:pos]; + [buffer replaceCharactersInRange:NSMakeRange(pos, 0) withString:buffer.lineEnding]; + [buffer endEditingAtIndex:_insertionPoint]; + + [self _moveCursor:pos + 1 preserveColumn:NO]; + [self _syncState]; +} + +- (void)_insertNewlineAboveLine:(NSUInteger)line +{ + XVimBuffer *buffer = self.buffer; + NSUInteger pos = [self.buffer indexOfLineNumber:line]; + + if (NSNotFound == pos) { + return; + } + if (line == 1) { + [buffer beginEditingAtIndex:0]; + [buffer replaceCharactersInRange:NSMakeRange(0, 0) withString:buffer.lineEnding]; + [buffer endEditingAtIndex:0]; + } else { + _insertionPoint = pos; + [self _insertNewlineBelowCurrentLine]; + } +} + +- (void)insertNewlineAboveAndInsertWithIndent +{ + NSUInteger head = [self.buffer startOfLine:_insertionPoint]; + + _cursorMode = CURSOR_MODE_INSERT; + if (head) { + [_textView setSelectedRange:NSMakeRange(head - 1,0)]; + [_textView insertNewline:self]; + } else { + [_textView setSelectedRange:NSMakeRange(0, 0)]; + [_textView insertNewline:self]; + [_textView setSelectedRange:NSMakeRange(0, 0)]; + } +} + +- (void)insertNewlineBelowAndInsertWithIndent +{ + NSUInteger tail = [self.buffer endOfLine:_insertionPoint]; + + _cursorMode = CURSOR_MODE_INSERT; + [_textView setSelectedRange:NSMakeRange(tail, 0)]; + [_textView insertNewline:_textView]; +} + +- (void)doInsert:(XVimInsertionPoint)mode blockColumn:(NSUInteger *)column blockLines:(XVimRange *)lines +{ + XVimBuffer *buffer = self.buffer; + + if (column) *column = NSNotFound; + if (lines) *lines = XVimMakeRange(NSNotFound, NSNotFound); + + if (self.selectionMode == XVIM_VISUAL_BLOCK) { + XVimSelection sel = [self _selectedBlock]; + NSUInteger tl = [buffer indexOfLineNumber:sel.top column:sel.left]; + + if (lines) *lines = XVimMakeRange(sel.top, sel.bottom); + switch (mode) { + case XVIM_INSERT_BLOCK_KILL: + [self _yankSelection:sel]; + [buffer beginEditingAtIndex:tl]; + [self _killSelection:sel]; + [buffer endEditingAtIndex:tl]; + /* falltrhough */ + case XVIM_INSERT_DEFAULT: + _insertionPoint = tl; + if (column) *column = sel.left; + break; + case XVIM_INSERT_APPEND: + if (sel.right != XVimSelectionEOL) { + sel.right++; + } + _insertionPoint = [buffer indexOfLineNumber:sel.top column:sel.right]; + if (column) *column = sel.right; + break; + default: + NSAssert(false, @"unreachable"); + break; + } + } else if (mode != XVIM_INSERT_DEFAULT) { + NSUInteger pos = _insertionPoint; + switch (mode) { + case XVIM_INSERT_APPEND_EOL: + _insertionPoint = [buffer endOfLine:pos]; + break; + case XVIM_INSERT_APPEND: + NSAssert(_cursorMode == CURSOR_MODE_COMMAND, @"_cursorMode shoud be CURSOR_MODE_COMMAND"); + if (![buffer isIndexAtEndOfLine:pos]) { + _insertionPoint = pos + 1; + } + break; + case XVIM_INSERT_BEFORE_FIRST_NONBLANK: + _insertionPoint = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; + break; + default: + NSAssert(false, @"unreachable"); + } + } + + _cursorMode = CURSOR_MODE_INSERT; + self.selectionMode = XVIM_VISUAL_NONE; + [self _syncState]; +} + +- (BOOL)doIncrementNumber:(int64_t)offset +{ + XVimBuffer *buffer = self.buffer; + NSUInteger pos; + + pos = [buffer incrementNumberAtIndex:_insertionPoint by:offset]; + if (pos == NSNotFound) { + return NO; + } + [self _moveCursor:pos preserveColumn:NO]; + [self _syncState]; + return YES; +} + +- (void)doInsertFixupWithText:(NSString *)text mode:(XVimInsertionPoint)mode + count:(NSUInteger)count column:(NSUInteger)column lines:(XVimRange)lines +{ + XVimBuffer *buffer = self.buffer; + NSMutableString *buf = nil; + NSUInteger tabWidth = buffer.tabWidth; + + if (count == 0 || lines.begin > lines.end || text.length == 0) { + return; + } + if ([text rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location != NSNotFound) { + return; + } + if (count > 1) { + buf = [[NSMutableString alloc] initWithCapacity:text.length * count]; + for (NSUInteger i = 0; i < count; i++) { + [buf appendString:text]; + } + text = buf; + } + + [buffer beginEditingAtIndex:NSNotFound]; + for (NSUInteger line = lines.begin; line <= lines.end; line++) { + NSUInteger pos = [buffer indexOfLineNumber:line column:column]; + + if (column != XVimSelectionEOL && [buffer isIndexAtEndOfLine:pos]) { + if ([buffer columnOfIndex:pos] < column) { + if (mode != XVIM_INSERT_APPEND) { + continue; + } + [buffer replaceCharactersInRange:NSMakeRange(pos, 0) + withSpaces:column - [buffer columnOfIndex:pos]]; + } + } + if (tabWidth && [buffer.string characterAtIndex:pos] == '\t') { + NSUInteger col = [buffer columnOfIndex:pos]; + + if (col < column) { + [buffer replaceCharactersInRange:NSMakeRange(pos, 1) + withSpaces:tabWidth - (col % tabWidth)]; + pos += column - col; + } + } + [buffer replaceCharactersInRange:NSMakeRange(pos, 0) withString:text]; + } + [buffer endEditingAtIndex:NSNotFound]; + + [buf release]; +} + +- (void)doSortLines:(XVimRange)range withOptions:(XVimSortOptions)options +{ + XVimBuffer *buffer = self.buffer; + NSUInteger pos; + + NSAssert(range.begin > 0 && range.end > 0, @"lines must be greater than 0."); + + if (range.begin > range.end) { + range = XVimRangeSwap(range); + } + + NSMutableArray *lines = [[NSMutableArray alloc] initWithCapacity:range.end - range.begin + 1]; + NSRange characterRange = [buffer indexRangeForLines:XVimMakeNSRange(range)]; + + pos = [buffer indexOfLineNumber:range.begin]; + for (NSUInteger line = range.begin; line <= range.end; line++) { + NSUInteger nlLen; + NSRange lineRange; + + lineRange = [buffer indexRangeForLineAtIndex:pos newLineLength:&nlLen]; + if (line == range.end && nlLen == 0 && lineRange.length == 0) { + break; + } + [lines addObject:[buffer.string substringWithRange:lineRange]]; + pos = NSMaxRange(lineRange) + nlLen; + } + + [lines sortUsingComparator:^NSComparisonResult(NSString *str1, NSString *str2) { + NSStringCompareOptions compareOptions = 0; + if (options & XVimSortOptionNumericSort) { + compareOptions |= NSNumericSearch; + } + if (options & XVimSortOptionIgnoreCase) { + compareOptions |= NSCaseInsensitiveSearch; + } + + if (options & XVimSortOptionReversed) { + return [str2 compare:str1 options:compareOptions]; + } else { + return [str1 compare:str2 options:compareOptions]; + } + }]; + + if (options & XVimSortOptionRemoveDuplicateLines) { + NSMutableIndexSet *removeIndices = [NSMutableIndexSet indexSet]; + // At this point the lines are already sorted + [lines enumerateObjectsUsingBlock:^(NSString *str, NSUInteger idx, BOOL *stop) { + if (idx < [lines count] - 1) { + NSString *nextStr = [lines objectAtIndex:idx + 1]; + if ([str isEqualToString:nextStr]) { + [removeIndices addIndex:idx + 1]; + } + } + }]; + [lines removeObjectsAtIndexes:removeIndices]; + } + + NSString *nl = buffer.lineEnding; + NSString *str = [[lines componentsJoinedByString:nl] stringByAppendingString:nl]; + + pos = characterRange.location; + [buffer beginEditingAtIndex:pos]; + [buffer replaceCharactersInRange:characterRange withString:str]; + [buffer endEditingAtIndex:pos]; + + [self _moveCursor:pos preserveColumn:NO]; + [self _syncState]; + + [lines release]; +} + +#pragma mark *** Drawing *** + +- (NSRect)_glyphRectAtIndex:(NSUInteger)index length:(NSUInteger)length +{ + if (length && index + length >= _textView.textStorage.length) { + // When the index is EOF the range to specify here can not be grater than 0. + // If it is greater than 0 it returns (0,0) as a glyph rect. + length = 0; + } + return [_textView.layoutManager boundingRectForGlyphRange:NSMakeRange(index, length) + inTextContainer:_textView.textContainer]; +} + +- (NSUInteger)_glyphHeightAtIndex:(NSUInteger)index +{ + return NSHeight([self _glyphRectAtIndex:index length:0]); +} + +- (NSUInteger)_lineNumberAtPoint:(NSPoint)point +{ + NSUInteger index; + + index = [_textView.enclosingScrollView.documentView characterIndexForInsertionAtPoint:point]; + return [self.buffer lineNumberAtIndex:index]; +} + +- (NSUInteger)lineNumberInScrollView:(CGFloat)ratio offset:(NSInteger)offset +{ + NSScrollView *scrollView = _textView.enclosingScrollView; + NSRect visibleRect = scrollView.contentView.bounds; + NSPoint point = visibleRect.origin; + CGFloat glyphHeight = [self _glyphHeightAtIndex:_insertionPoint]; + + NSInteger minLine, line, maxLine; + + minLine = (NSInteger)[self _lineNumberAtPoint:point]; + point.y += ratio * (NSHeight(visibleRect) - glyphHeight); + line = (NSInteger)[self _lineNumberAtPoint:point]; + point.y = NSMaxY(visibleRect) - glyphHeight; + maxLine = (NSInteger)[self _lineNumberAtPoint:point]; + + return (NSUInteger)MIN(maxLine, MAX(minLine, line + offset)); +} + +- (NSUInteger)_glyphIndexForPoint:(NSPoint)point +{ + return [_textView.layoutManager glyphIndexForPoint:point inTextContainer:_textView.textContainer]; +} + +- (void)drawInsertionPointInRect:(NSRect)rect color:(NSColor *)color + heightRatio:(CGFloat)heightRatio + widthRatio:(CGFloat)widthRatio + alpha:(CGFloat)alpha +{ + NSRect glyphRect = [self _glyphRectAtIndex:_insertionPoint length:1]; + + [[color colorWithAlphaComponent:alpha] set]; + rect.size.width = rect.size.height/2; + if (glyphRect.size.width > 0 && glyphRect.size.width < rect.size.width) { + rect.size.width = glyphRect.size.width; + } + + rect.origin.y += (1 - heightRatio) * rect.size.height; + rect.size.height *= heightRatio; + rect.size.width *= widthRatio; + + NSRectFillUsingOperation(rect, NSCompositeSourceOver); +} + +#pragma mark *** Scrolling *** + +- (void)_lineUp:(NSUInteger)index count:(NSUInteger)count +{ + [_textView scrollLineUp:_textView]; + + NSRect visibleRect = _textView.enclosingScrollView.contentView.bounds; + NSRect cursorRect = [self _glyphRectAtIndex:index length:0]; + if (NSMaxY(cursorRect) > NSMaxY(visibleRect)) { + [_textView moveUp:self]; + } +} + +- (void)scrollLineBackward:(NSUInteger)count +{ + [self _lineUp:_insertionPoint count:count]; +} + +- (void)_lineDown:(NSUInteger)index count:(NSUInteger)count +{ + [_textView scrollLineDown:_textView]; + + NSRect visibleRect = _textView.enclosingScrollView.contentView.bounds; + NSRect cursorRect = [self _glyphRectAtIndex:index length:0]; + if (NSMinY(cursorRect) < NSMinY(visibleRect)) { + [_textView moveDown:_textView]; + } +} + +- (void)scrollLineForward:(NSUInteger)count +{ + [self _lineDown:_insertionPoint count:count]; +} + +- (void)_scroll:(CGFloat)ratio count:(NSUInteger)count +{ + NSScrollView *scrollView = _textView.enclosingScrollView; + NSClipView *clipView = scrollView.contentView; + XVimBuffer *buffer = self.buffer; + + NSRect visibleRect = clipView.bounds; + CGFloat scrollSize = NSHeight(visibleRect) * ratio * count; + // This may be beyond the beginning or end of document (intentionally) + NSPoint scrollPoint = NSMakePoint(visibleRect.origin.x, visibleRect.origin.y + scrollSize); + + // Cursor position relative to left-top origin shold be kept after scroll + // (Exception is when it scrolls beyond the beginning or end of document) + + NSRect currentInsertionRect = [self _glyphRectAtIndex:_insertionPoint length:1]; + NSPoint relativeInsertionPoint = SubPoint(currentInsertionRect.origin, visibleRect.origin); + + // Cursor Position after scroll + NSPoint cursorAfterScroll = AddPoint(scrollPoint, relativeInsertionPoint); + + // Nearest character index to the cursor position after scroll + // TODO: consider blank-EOF line. Xcode does not return blank-EOF index with following method... + NSUInteger cursorIndexAfterScroll = [self _glyphIndexForPoint:cursorAfterScroll]; + + // We do not want to change the insert point relative position from top of visible rect + // We have to calc the distance between insertion point befor/after scrolling to keep the position. + NSRect insertionRectAfterScroll = [self _glyphRectAtIndex:cursorIndexAfterScroll length:1]; + NSPoint relativeInsertionPointAfterScroll = SubPoint(insertionRectAfterScroll.origin, scrollPoint); + CGFloat maxScrollY = NSHeight([scrollView.documentView frame]) - NSHeight(visibleRect); + + scrollPoint.y += relativeInsertionPointAfterScroll.y - relativeInsertionPoint.y; + if (scrollPoint.y > maxScrollY) { + // Prohibit scroll beyond the bounds of document + scrollPoint.y = maxScrollY; + } else if (scrollPoint.y < 0.0) { + scrollPoint.y = 0.0; + } + + [[scrollView contentView] scrollToPoint:scrollPoint]; + [scrollView reflectScrolledClipView:[scrollView contentView]]; + + cursorIndexAfterScroll = [buffer firstNonblankInLineAtIndex:cursorIndexAfterScroll allowEOL:YES]; + [self _moveCursor:cursorIndexAfterScroll preserveColumn:NO]; + [self _syncState]; +} + +- (void)scrollPageForward:(NSUInteger)count +{ + [self _scroll:1.0 count:count]; +} + +- (void)scrollPageBackward:(NSUInteger)count +{ + [self _scroll:-1.0 count:count]; +} + +- (void)scrollHalfPageForward:(NSUInteger)count +{ + [self _scroll:0.5 count:count]; +} + +- (void)scrollHalfPageBackward:(NSUInteger)count +{ + [self _scroll:-0.5 count:count]; +} + +- (void)_scrollCommon_moveCursorPos:(NSUInteger)lineNumber + ratio:(CGFloat)ratio + firstNonblank:(BOOL)fnb +{ + XVimBuffer *buffer = self.buffer; + NSUInteger pos = _insertionPoint; + + if (lineNumber) { + if ((pos = [buffer indexOfLineNumber:lineNumber]) == NSNotFound) { + pos = buffer.length; + } + } + if (fnb) { + pos = [buffer firstNonblankInLineAtIndex:pos allowEOL:YES]; + } + [self _moveCursor:pos preserveColumn:NO]; + [self _syncState]; + + NSRect glyphRect = [self _glyphRectAtIndex:_insertionPoint length:0]; + + NSScrollView *scrollView = _textView.enclosingScrollView; + NSClipView *clipView = scrollView.contentView; + + NSPoint point = NSMakePoint(0., NSMaxY(glyphRect)); + CGFloat deltay = ratio * NSHeight(_textView.enclosingScrollView.contentView.bounds); + point.y = point.y > deltay ? point.y - deltay : 0.; + + [clipView scrollToPoint:point]; + [scrollView reflectScrolledClipView:clipView]; +} + +- (void)scrollBottom:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb // zb / z- +{ + [self _scrollCommon_moveCursorPos:lineNumber ratio:1. firstNonblank:fnb]; +} + +- (void)scrollCenter:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb // zz / z. +{ + [self _scrollCommon_moveCursorPos:lineNumber ratio:.5 firstNonblank:fnb]; +} + +- (void)scrollTop:(NSUInteger)lineNumber firstNonblank:(BOOL)fnb // zt / z +{ + [self _scrollCommon_moveCursorPos:lineNumber ratio:0. firstNonblank:fnb]; +} + +- (void)scrollTo:(NSUInteger)location +{ + // Update: I do not know if we really need Following block. + // It looks that they need it to call ensureLayoutForGlyphRange but do not know when it needed + // What I changed was the way calc "glyphRec". Not its using [self boundingRectForGlyphIndex] which coniders + // text folding when calc the rect. + /* + BOOL isBlankline = + (location == [[self string] length] || isNewline([[self string] characterAtIndex:location])) && + (location == 0 || isNewline([[self string] characterAtIndex:location-1])); + + NSRange characterRange; + characterRange.location = location; + characterRange.length = isBlankline ? 0 : 1; + + // Must call ensureLayoutForGlyphRange: to fix a bug where it will not scroll + // to the appropriate glyph due to non contiguous layout + NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:characterRange actualCharacterRange:NULL]; + [[self layoutManager] ensureLayoutForGlyphRange:NSMakeRange(0, glyphRange.location + glyphRange.length)]; + */ + + NSScrollView *scrollView = _textView.enclosingScrollView; + NSClipView *clipView = scrollView.contentView; + + NSRect glyphRect = [self _glyphRectAtIndex:location length:1]; + CGFloat glyphLeft = NSMinX(glyphRect); + CGFloat glyphRight = NSMaxX(glyphRect); + CGFloat glyphBottom = NSMaxY(glyphRect); + CGFloat glyphTop = NSMinY(glyphRect); + + NSRect contentRect = clipView.bounds; + CGFloat viewLeft = NSMinX(contentRect); + CGFloat viewRight = NSMaxX(contentRect); + CGFloat viewTop = NSMinY(contentRect); + CGFloat viewBottom = NSMaxY(contentRect); + + NSPoint scrollPoint = contentRect.origin; + if (glyphRight > viewRight) { + scrollPoint.x = glyphLeft - NSWidth(contentRect) / 2.; + } else if (glyphLeft < viewLeft) { + scrollPoint.x = glyphRight - NSWidth(contentRect) / 2.; + } + scrollPoint.x = MAX(0, scrollPoint.x); + + if (glyphTop < viewTop) { + if (viewTop - glyphTop > NSHeight(contentRect)){ + scrollPoint.y = glyphBottom - NSHeight(contentRect) / 2.; + } else { + scrollPoint.y = glyphTop; + } + } else if (glyphBottom > viewBottom) { + if (glyphBottom - viewBottom > NSHeight(contentRect)) { + scrollPoint.y = glyphBottom - NSHeight(contentRect) / 2.; + } else { + scrollPoint.y = glyphBottom - NSHeight(contentRect); + } + } + scrollPoint.y = MAX(0, scrollPoint.y); + + [clipView scrollToPoint:scrollPoint]; + [scrollView reflectScrolledClipView:clipView]; +} + +#pragma mark *** Crap to sort *** + +- (void)xvim_setWrapsLines:(BOOL)wraps +{ +#ifdef __USE_DVTKIT__ + if ([_textView isKindOfClass:[DVTSourceTextView class]]) { + [(DVTSourceTextView *)_textView setWrapsLines:wraps]; + } +#endif +} + +- (void)xvim_hideCompletions +{ +#ifdef __USE_DVTKIT__ + if ([_textView isKindOfClass:[DVTSourceTextView class]]) { + [((DVTSourceTextView *)_textView).completionController hideCompletions]; + } +#endif +} + +#pragma mark Search + +- (void)xvim_highlightNextSearchCandidate:(NSString *)regex count:(NSUInteger)count + option:(XVimMotionOptions)opt forward:(BOOL)forward +{ + NSTextStorage *ts = _textView.textStorage; + NSRange range = NSMakeRange(NSNotFound,0); + + if (forward) { + range = [ts searchRegexForward:regex from:_insertionPoint count:count option:opt]; + }else{ + range = [ts searchRegexBackward:regex from:_insertionPoint count:count option:opt]; + } + if (range.location != NSNotFound) { + [self scrollTo:range.location]; + [_textView showFindIndicatorForRange:range]; + } +} + +- (void)xvim_highlightNextSearchCandidateForward:(NSString*)regex count:(NSUInteger)count option:(XVimMotionOptions)opt +{ + [self xvim_highlightNextSearchCandidate:regex count:count option:opt forward:YES]; +} + +- (void)xvim_highlightNextSearchCandidateBackward:(NSString*)regex count:(NSUInteger)count option:(XVimMotionOptions)opt +{ + [self xvim_highlightNextSearchCandidate:regex count:count option:opt forward:NO]; +} + +- (void)xvim_updateFoundRanges:(NSString*)pattern withOption:(XVimMotionOptions)opt +{ + NSAssert( nil != pattern, @"pattern munst not be nil"); + + if (!_needsUpdateFoundRanges) { + return; + } + + NSRegularExpressionOptions r_opts = NSRegularExpressionAnchorsMatchLines; + if (opt & MOPT_SEARCH_CASEINSENSITIVE) { + r_opts |= NSRegularExpressionCaseInsensitive; + } + + NSError *error = nil; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:r_opts error:&error]; + + if (nil != error) { + [_foundRanges removeAllObjects]; + return; + } + + // Find all the maches + NSString *string = self.buffer.string; + if (string == nil) { + return; + } + + NSArray *matches = [regex matchesInString:string options:0 range:NSMakeRange(0, string.length)]; + [_foundRanges setArray:matches]; + + // Clear current highlight. + [self xvim_clearHighlightText]; + // Add yellow highlight + for (NSTextCheckingResult *result in self.foundRanges) { + [_textView.layoutManager addTemporaryAttribute:NSBackgroundColorAttributeName + value:[NSColor yellowColor] forCharacterRange:result.range]; + } + + _needsUpdateFoundRanges = NO; +} + +- (void)xvim_clearHighlightText +{ + if (!_needsUpdateFoundRanges) { + return; + } + + [_textView.layoutManager removeTemporaryAttribute:NSBackgroundColorAttributeName + forCharacterRange:NSMakeRange(0, self.buffer.length)]; + // [self.layoutManager addTemporaryAttribute:NSBackgroundColorAttributeName value:[NSColor clearColor] forCharacterRange:NSMakeRange(0, string.length)]; + _needsUpdateFoundRanges = NO; +} + +- (NSRange)xvim_currentWord:(XVimMotionOptions)opt +{ + return [_textView.textStorage currentWord:_insertionPoint count:1 option:opt|MOPT_TEXTOBJECT_INNER]; +} + +@end diff --git a/XVim/XVimVisualEvaluator.h b/XVim/XVimVisualEvaluator.h index 9814d149..173f9c7e 100644 --- a/XVim/XVimVisualEvaluator.h +++ b/XVim/XVimVisualEvaluator.h @@ -14,8 +14,7 @@ // They only rely on their interface to handle them. @interface XVimVisualEvaluator : XVimMotionEvaluator -- (id)initWithWindow:(XVimWindow*)window mode:(XVIM_VISUAL_MODE)mode; +- (id)initWithWindow:(XVimWindow*)window mode:(XVimVisualMode)mode; - (id)initWithLastVisualStateWithWindow:(XVimWindow *)window; - @end diff --git a/XVim/XVimVisualEvaluator.m b/XVim/XVimVisualEvaluator.m index 80a1ee12..31e5a929 100644 --- a/XVim/XVimVisualEvaluator.m +++ b/XVim/XVimVisualEvaluator.m @@ -23,109 +23,119 @@ #import "XVim.h" #import "XVimYankEvaluator.h" #import "XVimShiftEvaluator.h" -#import "XVimLowercaseEvaluator.h" -#import "XVimUppercaseEvaluator.h" -#import "XVimTildeEvaluator.h" +#import "XVimSwapCharsEvaluator.h" #import "XVimJoinEvaluator.h" -#import "NSTextView+VimOperation.h" +#import "XVimView.h" static NSString* MODE_STRINGS[] = {@"", @"-- VISUAL --", @"-- VISUAL LINE --", @"-- VISUAL BLOCK --"}; @interface XVimVisualEvaluator(){ BOOL _waitForArgument; - NSRange _operationRange; - XVIM_VISUAL_MODE _visual_mode; + XVimVisualInfo _initial; } -@property XVimPosition initialFromPos; -@property XVimPosition initialToPos; -@property BOOL initialToEOL; @end @implementation XVimVisualEvaluator - (id)initWithLastVisualStateWithWindow:(XVimWindow *)window{ - if( self = [self initWithWindow:window mode:[XVim instance].lastVisualMode] ){ - self.initialFromPos = [XVim instance].lastVisualSelectionBegin; - self.initialToPos = [XVim instance].lastVisualPosition; - self.initialToEOL = [XVim instance].lastVisualSelectionToEOL; + XVimBuffer *buffer = window.currentBuffer; + XVimVisualInfo *vi = &buffer->visualInfo; + + if (vi->mode == XVIM_VISUAL_NONE) { + [window errorMessage:@"no previous visual state" ringBell:YES]; + [self release]; + return nil; + } + + if ((self = [self initWithWindow:window])) { + _initial = *vi; } return self; } -- (id)initWithWindow:(XVimWindow *)window mode:(XVIM_VISUAL_MODE)mode { +- (id)initWithWindow:(XVimWindow *)window mode:(XVimVisualMode)mode { + NSTextView *sourceView = window.currentView.textView; + NSUInteger start = [[sourceView.selectedRanges objectAtIndex:0] rangeValue].location; + NSUInteger end = NSMaxRange([sourceView.selectedRanges.lastObject rangeValue]); + XVimBuffer *buffer = window.currentBuffer; + + if (end > start) { + end--; + } + if (self = [self initWithWindow:window]) { _waitForArgument = NO; - _visual_mode = mode; - if( [window.sourceView selectedRanges].count == 1 ){ - if( [window.sourceView selectedRange].length == 0 ){ - self.initialFromPos = XVimMakePosition(NSNotFound, NSNotFound);; - self.initialToPos = XVimMakePosition(NSNotFound, NSNotFound);; - }else{ - NSUInteger start = [window.sourceView selectedRange].location; - NSUInteger end = [window.sourceView selectedRange].location + [window.sourceView selectedRange].length - 1; - self.initialFromPos = XVimMakePosition([window.sourceView.textStorage xvim_lineNumberAtIndex:start], [window.sourceView.textStorage xvim_columnOfIndex:start]); - self.initialToPos = XVimMakePosition([window.sourceView.textStorage xvim_lineNumberAtIndex:end], [window.sourceView.textStorage xvim_columnOfIndex:end]); - } - }else{ + _initial.mode = mode; + _initial.start.line = [buffer lineNumberAtIndex:start]; + _initial.start.column = [buffer columnOfIndex:start]; + _initial.end.line = [buffer lineNumberAtIndex:end]; + _initial.end.column = [buffer columnOfIndex:end]; + _initial.colwant = _initial.end.column; + + if (sourceView.selectedRanges.count > 1) { // Treat it as block selection - _visual_mode = XVIM_VISUAL_BLOCK; - NSUInteger start = [[[window.sourceView selectedRanges] objectAtIndex:0] rangeValue].location; - NSUInteger end = [[[window.sourceView selectedRanges] lastObject] rangeValue].location + [[[window.sourceView selectedRanges] lastObject] rangeValue].length - 1; - self.initialFromPos = XVimMakePosition([window.sourceView.textStorage xvim_lineNumberAtIndex:start], [window.sourceView.textStorage xvim_columnOfIndex:start]); - self.initialToPos = XVimMakePosition([window.sourceView.textStorage xvim_lineNumberAtIndex:end], [window.sourceView.textStorage xvim_columnOfIndex:end]); + _initial.mode = XVIM_VISUAL_BLOCK; } } return self; } - (NSString*)modeString { - return MODE_STRINGS[_visual_mode]; + return MODE_STRINGS[_initial.mode]; } - (XVIM_MODE)mode{ return XVIM_MODE_VISUAL; } -- (void)becameHandler{ +- (void)becameHandler +{ + XVimView *xview = self.currentView; + [super becameHandler]; - if( self.initialToPos.line != NSNotFound ){ - if( XVim.instance.isRepeating ){ - [self.sourceView xvim_changeSelectionMode:_visual_mode]; + + if (_initial.mode) { + if (XVim.instance.isRepeating) { + NSUInteger columns = XVimVisualInfoColumns(&_initial); + NSUInteger lines = XVimVisualInfoLines(&_initial); + XVimPosition pos; + + xview.selectionMode = _initial.mode; + pos = xview.insertionPosition; + // When repeating we have to set initial selected range - if( _visual_mode == XVIM_VISUAL_CHARACTER ){ - if( self.initialFromPos.line == self.initialToPos.line ){ + if (_initial.mode == XVIM_VISUAL_CHARACTER) { + if (lines == 1) { // Same number of character if in one line - NSUInteger numberOfColumns = self.initialToPos.column > self.initialFromPos.column ? (self.initialToPos.column - self.initialFromPos.column) : (self.initialFromPos.column - self.initialToPos.column); - [self.sourceView xvim_moveToPosition:XVimMakePosition(self.sourceView.insertionLine, self.sourceView.insertionColumn+numberOfColumns)]; - }else{ - NSUInteger numberOfLines = self.initialToPos.line > self.initialFromPos.line ? (self.initialToPos.line - self.initialFromPos.line) : (self.initialFromPos.line - self.initialToPos.line); - [self.sourceView xvim_moveToPosition:XVimMakePosition(self.sourceView.insertionLine+numberOfLines, self.initialToPos.column)]; + pos.column += columns - 1; + } else { + pos.line += lines - 1; + pos.column = _initial.start.line < _initial.end.line ? _initial.end.column : _initial.start.column; } - }else if( _visual_mode == XVIM_VISUAL_LINE ){ - // Same number of lines - NSUInteger numberOfLines = self.initialToPos.line > self.initialFromPos.line ? (self.initialToPos.line - self.initialFromPos.line) : (self.initialFromPos.line - self.initialToPos.line); - [self.sourceView xvim_moveToPosition:XVimMakePosition(self.sourceView.insertionLine+numberOfLines, self.sourceView.insertionColumn)]; - }else if( _visual_mode == XVIM_VISUAL_BLOCK ){ + } else if (_initial.mode == XVIM_VISUAL_LINE) { + pos.line += lines - 1; + } else { // Use same number of lines/colums - NSUInteger numberOfLines = self.initialToPos.line > self.initialFromPos.line ? (self.initialToPos.line - self.initialFromPos.line) : (self.initialFromPos.line - self.initialToPos.line); - NSUInteger numberOfColumns = self.initialToPos.column > self.initialFromPos.column ? (self.initialToPos.column - self.initialFromPos.column) : (self.initialFromPos.column - self.initialToPos.column); - [self.sourceView xvim_moveToPosition:XVimMakePosition(self.sourceView.insertionLine+numberOfLines, self.sourceView.insertionColumn+numberOfColumns)]; + pos.column += columns - 1; + pos.line += lines - 1; } - }else{ - [self.sourceView xvim_moveToPosition:self.initialFromPos]; - [self.sourceView xvim_changeSelectionMode:_visual_mode]; - [self.sourceView xvim_moveToPosition:self.initialToPos]; + + [xview moveCursorToPosition:pos]; + } else { + [xview moveCursorToPosition:_initial.start]; + xview.selectionMode = _initial.mode; + [xview moveCursorToPosition:_initial.end]; + // TODO: self.sourceView.preservedColumn = _initial.colwant; } - }else{ - [self.sourceView xvim_changeSelectionMode:_visual_mode]; - } - if (self.initialToEOL) { - [self performSelector:@selector(DOLLAR)]; + if (_initial.end.column == XVimSelectionEOL) { + [self performSelector:@selector(DOLLAR)]; + } + _initial.mode = XVIM_VISUAL_NONE; } } - (void)didEndHandler{ if (!_waitForArgument) { - [self.sourceView xvim_changeSelectionMode:XVIM_VISUAL_NONE]; + self.currentView.selectionMode = XVIM_VISUAL_NONE; // TODO: //[[[XVim instance] repeatRegister] setVisualMode:_mode withRange:_operationRange]; } @@ -137,10 +147,9 @@ - (XVimKeymap*)selectKeymapWithProvider:(id)keymapProvider { } - (XVimEvaluator*)eval:(XVimKeyStroke*)keyStroke{ - [XVim instance].lastVisualMode = self.sourceView.selectionMode; - [XVim instance].lastVisualPosition = self.sourceView.insertionPosition; - [XVim instance].lastVisualSelectionBegin = self.sourceView.selectionBeginPosition; - [XVim instance].lastVisualSelectionToEOL = self.sourceView.selectionToEOL; + if (!XVim.instance.isRepeating) { + [self.currentView saveVisualInfoForBuffer:self.window.currentBuffer]; + } XVimEvaluator *nextEvaluator = [super eval:keyStroke]; @@ -181,34 +190,37 @@ - (XVimEvaluator*)onComplete_ai:(XVimTextObjectEvaluator*)childEvaluator{ } - (XVimEvaluator*)c{ - if (self.sourceView.selectionMode == XVIM_VISUAL_BLOCK) { + if (self.currentView.inBlockMode) { return [[[XVimInsertEvaluator alloc] initWithWindow:self.window oneCharMode:NO mode:XVIM_INSERT_BLOCK_KILL] autorelease]; } XVimDeleteEvaluator* eval = [[[XVimDeleteEvaluator alloc] initWithWindow:self.window insertModeAtCompletion:YES] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, 1)]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, 1)]; } - (XVimEvaluator *)C{ - if (self.sourceView.selectionMode != XVIM_VISUAL_BLOCK) { - [self.sourceView xvim_changeSelectionMode:XVIM_VISUAL_LINE]; + XVimView *xview = self.currentView; + + if (!xview.inBlockMode) { + xview.selectionMode = XVIM_VISUAL_LINE; return [self c]; } - return [[[XVimInsertEvaluator alloc] initWithWindow:self.window oneCharMode:NO mode:XVIM_INSERT_BLOCK_KILL_EOL] autorelease]; + [self performSelector:@selector(DOLLAR)]; + return [[[XVimInsertEvaluator alloc] initWithWindow:self.window oneCharMode:NO mode:XVIM_INSERT_BLOCK_KILL] autorelease]; } - (XVimEvaluator*)C_b{ - [[self sourceView] xvim_scrollPageBackward:[self numericArg]]; + [self.currentView scrollPageBackward:self.numericArg]; return self; } - (XVimEvaluator*)C_d{ - [[self sourceView] xvim_scrollHalfPageForward:[self numericArg]]; + [self.currentView scrollHalfPageForward:self.numericArg]; return self; } - (XVimEvaluator*)d{ XVimDeleteEvaluator* eval = [[[XVimDeleteEvaluator alloc] initWithWindow:self.window] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, 0)]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOPT_NONE, 0)]; } - (XVimEvaluator *)DEL{ @@ -216,18 +228,19 @@ - (XVimEvaluator *)DEL{ } - (XVimEvaluator*)D{ - if (self.sourceView.selectionMode != XVIM_VISUAL_BLOCK) { - [self.sourceView xvim_changeSelectionMode:XVIM_VISUAL_LINE]; - return [self d]; + XVimView *xview = self.currentView; + + if (xview.inBlockMode) { + [self performSelector:@selector(DOLLAR)]; + } else { + xview.selectionMode = XVIM_VISUAL_LINE; } - // FIXME: doesn't work ask expected in any visual mode - XVimDeleteEvaluator* eval = [[[XVimDeleteEvaluator alloc] initWithWindow:self.window] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, LINEWISE, MOTION_OPTION_NONE, 0)]; + return [self d]; } - (XVimEvaluator*)C_f{ - [[self sourceView] xvim_scrollPageForward:[self numericArg]]; + [self.currentView scrollPageForward:self.numericArg]; return self; } @@ -246,7 +259,7 @@ - (XVimEvaluator *)g_completed:(XVimEvaluator *)childEvaluator{ } - (XVimEvaluator*)I{ - if (self.sourceView.selectionMode != XVIM_VISUAL_BLOCK) { + if (!self.currentView.inBlockMode) { return [[[XVimInsertEvaluator alloc] initWithWindow:self.window oneCharMode:NO mode:XVIM_INSERT_BEFORE_FIRST_NONBLANK] autorelease]; } return [[[XVimInsertEvaluator alloc] initWithWindow:self.window oneCharMode:NO mode:XVIM_INSERT_DEFAULT] autorelease]; @@ -254,7 +267,7 @@ - (XVimEvaluator*)I{ - (XVimEvaluator*)J{ XVimJoinEvaluator* eval = [[[XVimJoinEvaluator alloc] initWithWindow:self.window addSpace:YES] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, self.numericArg)]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, self.numericArg)]; } - (XVimEvaluator*)m{ @@ -271,19 +284,18 @@ - (XVimEvaluator*)m_completed:(XVimEvaluator*)childEvaluator{ } - (XVimEvaluator *)o{ - [self.sourceView xvim_selectSwapEndsOnSameLine:NO]; + [self.currentView selectSwapCorners:NO]; return self; } - (XVimEvaluator *)O{ - [self.sourceView xvim_selectSwapEndsOnSameLine:YES]; + [self.currentView selectSwapCorners:YES]; return self; } - (XVimEvaluator*)p{ - NSTextView* view = [self sourceView]; XVimRegister* reg = [[[XVim instance] registerManager] registerByName:self.yankRegister]; - [view xvim_put:reg.string withType:reg.type afterCursor:YES count:[self numericArg]]; + [self.currentView doPut:reg.string withType:reg.type afterCursor:YES count:[self numericArg]]; return nil; } @@ -307,52 +319,57 @@ - (XVimEvaluator*)s{ } - (XVimEvaluator *)S{ - if (self.sourceView.selectionMode != XVIM_VISUAL_BLOCK) { - [self.sourceView xvim_changeSelectionMode:XVIM_VISUAL_LINE]; + XVimView *xview = self.currentView; + + if (!xview.inBlockMode) { + xview.selectionMode = XVIM_VISUAL_LINE; } [self.window errorMessage:@"{Visual}S not implemented yet" ringBell:NO]; return self; } - (XVimEvaluator*)u{ - XVimLowercaseEvaluator* eval = [[[XVimLowercaseEvaluator alloc] initWithWindow:self.window] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, self.numericArg)]; + XVimSwapCharsEvaluator *eval = [[[XVimSwapCharsEvaluator alloc] initWithWindow:self.window mode:XVIM_BUFFER_SWAP_LOWER] autorelease]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOPT_NONE, self.numericArg)]; } - (XVimEvaluator*)U{ - XVimUppercaseEvaluator* eval = [[[XVimUppercaseEvaluator alloc] initWithWindow:self.window] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, self.numericArg)]; + XVimSwapCharsEvaluator *eval = [[[XVimSwapCharsEvaluator alloc] initWithWindow:self.window mode:XVIM_BUFFER_SWAP_UPPER] autorelease]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOPT_NONE, self.numericArg)]; } - (XVimEvaluator*)C_u{ - [[self sourceView] xvim_scrollHalfPageBackward:[self numericArg]]; + [self.currentView scrollHalfPageBackward:self.numericArg]; return self; } - (XVimEvaluator*)v{ - NSTextView *view = [self sourceView]; - if( view.selectionMode == XVIM_VISUAL_CHARACTER ){ - return [self ESC]; + XVimView *xview = self.currentView; + + if (xview.selectionMode == XVIM_VISUAL_CHARACTER) { + return [self ESC]; } - [view xvim_changeSelectionMode:XVIM_VISUAL_CHARACTER]; + xview.selectionMode = XVIM_VISUAL_CHARACTER; return self; } - (XVimEvaluator*)V{ - NSTextView *view = [self sourceView]; - if( view.selectionMode == XVIM_VISUAL_LINE){ + XVimView *xview = self.currentView; + + if (xview.selectionMode == XVIM_VISUAL_LINE) { return [self ESC]; } - [view xvim_changeSelectionMode:XVIM_VISUAL_LINE]; + xview.selectionMode = XVIM_VISUAL_LINE; return self; } - (XVimEvaluator*)C_v{ - NSTextView *view = [self sourceView]; - if( view.selectionMode == XVIM_VISUAL_BLOCK){ + XVimView *xview = self.currentView; + + if (xview.selectionMode == XVIM_VISUAL_BLOCK) { return [self ESC]; } - [view xvim_changeSelectionMode:XVIM_VISUAL_BLOCK]; + xview.selectionMode = XVIM_VISUAL_BLOCK; return self; } @@ -361,20 +378,24 @@ - (XVimEvaluator*)x{ } - (XVimEvaluator*)X{ - if (self.sourceView.selectionMode != XVIM_VISUAL_BLOCK) { - [self.sourceView xvim_changeSelectionMode:XVIM_VISUAL_LINE]; + XVimView *xview = self.currentView; + + if (!xview.inBlockMode) { + xview.selectionMode = XVIM_VISUAL_LINE; } return [self d]; } - (XVimEvaluator*)y{ - [[self sourceView] xvim_yank:nil]; + [self.currentView doYank:nil]; return nil; } - (XVimEvaluator*)Y{ - if (self.sourceView.selectionMode != XVIM_VISUAL_BLOCK) { - [self.sourceView xvim_changeSelectionMode:XVIM_VISUAL_LINE]; + XVimView *xview = self.currentView; + + if (!xview.inBlockMode) { + xview.selectionMode = XVIM_VISUAL_LINE; } return [self y]; } @@ -424,11 +445,11 @@ - (XVimEvaluator*)DQUOTE:(XVimWindow*)window{ - (XVimEvaluator*)EQUAL{ XVimEqualEvaluator* eval = [[[XVimEqualEvaluator alloc] initWithWindow:self.window] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)ESC{ - [[self sourceView] xvim_changeSelectionMode:XVIM_VISUAL_NONE]; + self.currentView.selectionMode = XVIM_VISUAL_NONE; return nil; } @@ -453,9 +474,8 @@ - (XVimEvaluator*)COLON{ { XVimExCommand *excmd = [[XVim instance] excmd]; [excmd executeCommand:command inWindow:self.window]; - - //NSTextView *sourceView = [window sourceView]; - [[self sourceView] xvim_changeSelectionMode:XVIM_VISUAL_NONE]; + + self.currentView.selectionMode = XVIM_VISUAL_NONE; return nil; } onKeyPress:nil]; @@ -470,12 +490,12 @@ - (XVimEvaluator *)EXCLAMATION{ - (XVimEvaluator*)GREATERTHAN{ XVimShiftEvaluator* eval = [[[XVimShiftEvaluator alloc] initWithWindow:self.window unshift:NO] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, self.numericArg)]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOPT_NONE, self.numericArg)]; } - (XVimEvaluator*)LESSTHAN{ XVimShiftEvaluator* eval = [[[XVimShiftEvaluator alloc] initWithWindow:self.window unshift:YES] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOTION_OPTION_NONE, self.numericArg)]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_INCLUSIVE, MOPT_NONE, self.numericArg)]; } - (XVimEvaluator*)executeSearch:(XVimWindow*)window firstLetter:(NSString*)firstLetter { @@ -551,13 +571,13 @@ - (XVimEvaluator*)SLASH{ } - (XVimEvaluator*)TILDE{ - XVimTildeEvaluator* eval = [[[XVimTildeEvaluator alloc] initWithWindow:self.window] autorelease]; - return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOTION_OPTION_NONE, [self numericArg])]; + XVimSwapCharsEvaluator *eval = [[[XVimSwapCharsEvaluator alloc] initWithWindow:self.window mode:XVIM_BUFFER_SWAP_CASE] autorelease]; + return [eval executeOperationWithMotion:XVIM_MAKE_MOTION(MOTION_NONE, CHARACTERWISE_EXCLUSIVE, MOPT_NONE, [self numericArg])]; } - (XVimEvaluator*)motionFixed:(XVimMotion *)motion{ if(!XVim.instance.isRepeating){ - [[self sourceView] xvim_move:motion]; + [self.currentView moveCursorWithMotion:motion]; [self resetNumericArg]; } [self.argumentString setString:@""]; diff --git a/XVim/XVimWindow+Xcode.h b/XVim/XVimWindow+Xcode.h deleted file mode 100644 index 651b87cc..00000000 --- a/XVim/XVimWindow+Xcode.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// XVimWindow+Xcode.h -// XVim -// -// Created by Suzuki Shuichiro on 9/18/12. -// -// - -#import "XVimWindow.h" - -@interface XVimWindow (Xcode) - -@end diff --git a/XVim/XVimWindow+Xcode.m b/XVim/XVimWindow+Xcode.m deleted file mode 100644 index 9c89e8a7..00000000 --- a/XVim/XVimWindow+Xcode.m +++ /dev/null @@ -1,13 +0,0 @@ -// -// XVimWindow+Xcode.m -// XVim -// -// Created by Suzuki Shuichiro on 9/18/12. -// -// - -#import "XVimWindow+Xcode.h" - -@implementation XVimWindow (Xcode) - -@end diff --git a/XVim/XVimWindow.h b/XVim/XVimWindow.h index 6b55ced0..04ab2fb0 100644 --- a/XVim/XVimWindow.h +++ b/XVim/XVimWindow.h @@ -7,8 +7,9 @@ #import #import "XVimCommandLine.h" -#import "XVimTextViewProtocol.h" #import "XVimKeyStroke.h" +#import "XVimBuffer.h" +#import "XVimView.h" /* * This class manages 1 window. (The term "window" here is different from NSWindow) @@ -17,7 +18,6 @@ * the associated XVimWindow object first and it handles the event. */ -@class XVimSourceView; @class XVimEvaluator; @class XVimRegister; @class IDEEditorArea; @@ -26,14 +26,15 @@ @class IDEEditorArea; @interface XVimWindow : NSObject -@property(readonly) NSTextView *sourceView; // This represents currently focused sourceView -@property(readonly) XVimCommandLine *commandLine; +@property(nonatomic, readonly) XVimCommandLine *commandLine; +@property(nonatomic, readonly) XVimBuffer *currentBuffer; +@property(nonatomic, readonly) XVimView *currentView; - (instancetype)initWithIDEEditorArea:(IDEEditorArea *)editorArea; - (void)handleKeyStroke:(XVimKeyStroke*)keyStroke onStack:(NSMutableArray*)stack; - (BOOL)handleKeyEvent:(NSEvent*)event; -- (NSRect)drawInsertionPointInRect:(NSRect)rect color:(NSColor*)color; +- (void)drawInsertionPointInRect:(NSRect)rect color:(NSColor*)color; - (void)errorMessage:(NSString *)message ringBell:(BOOL)ringBell; - (void)statusMessage:(NSString*)message; diff --git a/XVim/XVimWindow.m b/XVim/XVimWindow.m index 85628b12..24fc6c23 100644 --- a/XVim/XVimWindow.m +++ b/XVim/XVimWindow.m @@ -5,8 +5,10 @@ // Created by Tomas Lundell on 9/04/12. // +#import #import "XVimWindow.h" #import "XVim.h" +#import "XVimView.h" #import "XVimUtil.h" #import "XVimNormalEvaluator.h" #import "XVimVisualEvaluator.h" @@ -14,13 +16,38 @@ #import "XVimKeymap.h" #import "XVimOptions.h" #import "Logger.h" -#import -#import "IDEEditorArea+XVim.h" #import "XVimSearch.h" -#import "NSTextView+VimOperation.h" + +#import "IDEEditor+XVim.h" +#import "IDEEditorArea+XVim.h" +#import "DVTSourceTextScrollView+XVim.h" +#import "NSEvent+VimHelper.h" #import "XVimCommandLineEvaluator.h" #import "XVimInsertEvaluator.h" +@implementation IDEWorkspaceWindow (XVim) + ++ (void)xvim_initialize +{ +#if 0 // Only useful for debugging purposes + if (self == [IDEWorkspaceWindow class]) { + [self xvim_swizzleInstanceMethod:@selector(sendEvent:) + with:@selector(xvim_sendEvent:)]; + } +#endif +} + +- (void)xvim_sendEvent:(NSEvent *)event +{ + if (event.type == NSKeyDown) { + TRACE_LOG(@"Window:%p keyCode:%d characters:%@ charsIgnoreMod:%@ cASCII:%d", + self, event.keyCode, event.characters, event.charactersIgnoringModifiers, event.unmodifiedKeyCode); + } + [self xvim_sendEvent:event]; +} + +@end + @interface XVimWindow () { NSMutableArray *_evaluatorStack; XVimKeymapContext *_keymapContext; @@ -41,9 +68,22 @@ @implementation XVimWindow @synthesize commandLine = _commandLine; @synthesize tmpBuffer = _tmpBuffer; ++ (void)initialize +{ + if (self == [XVimWindow class]) { + [IDEWorkspaceWindow xvim_initialize]; + [IDEEditorArea xvim_initialize]; + [DVTSourceTextScrollView xvim_initialize]; + [IDEEditor xvim_initialize]; + [IDEComparisonEditor xvim_initialize]; + [XVimView class]; + } +} + - (instancetype)initWithIDEEditorArea:(IDEEditorArea *)editorArea { if (self = [super init]){ + DEBUG_LOG("Window %p created on %@", self, editorArea); _staticString = [@"" retain]; _keymapContext = [[XVimKeymapContext alloc] init]; _editorArea = [editorArea retain]; @@ -52,8 +92,8 @@ - (instancetype)initWithIDEEditorArea:(IDEEditorArea *)editorArea [self _resetEvaluatorStack:_evaluatorStack activateNormalHandler:YES]; _commandLine = [[XVimCommandLine alloc] init]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_documentChangedNotification:) - name:XVimDocumentChangedNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_bufferChangedNotification:) + name:XVimBufferChangedNotification object:nil]; } return self; } @@ -61,23 +101,30 @@ - (instancetype)initWithIDEEditorArea:(IDEEditorArea *)editorArea - (NSTextView *)sourceView { IDEEditor *editor = _editorArea.lastActiveEditorContext.editor; - id obj; - obj = _editorArea.workspaceTabController.windowController.window.firstResponder; - if ([obj isKindOfClass:[DVTSourceTextView class]]){ - return obj; + if ([editor isKindOfClass:[IDEComparisonEditor class]]) { + editor = [(id)editor keyEditor]; } - if (_editorArea.editorMode == 2 && [editor isKindOfClass:[IDEComparisonEditor class]]) { - obj = [[(IDEComparisonEditor *)editor keyEditor] mainScrollView].documentView; - return obj; + if ([editor isKindOfClass:NSClassFromString(@"IDESourceCodeEditor")]) { + return [(id)editor textView]; } + return nil; +} - return editor.mainScrollView.documentView; +- (XVimView *)currentView +{ + return self.sourceView.xvim_view; +} + +- (XVimBuffer *)currentBuffer +{ + return self.currentView.buffer; } - (void)dealloc { + DEBUG_LOG("Window %p deleted", self); [[NSNotificationCenter defaultCenter] removeObserver:self]; [_keymapContext release]; [_staticString release]; @@ -91,11 +138,13 @@ - (void)dealloc - (void)dumpEvaluatorStack:(NSMutableArray*)stack { +#ifdef DEBUG for (NSUInteger i = 0; i < stack.count; i++) { XVimEvaluator *e = [stack objectAtIndex:i]; DEBUG_LOG(@"Evaluator%d:%@ argStr:%@ yankReg:%@", i, NSStringFromClass([e class]), e.argumentString, e.yankRegister); } +#endif } #pragma mark - Handling keystrokes and evaluation stack @@ -116,7 +165,7 @@ - (void)_resetEvaluatorStack:(NSMutableArray *)stack activateNormalHandler:(BOOL } } -- (void)_documentChangedNotification:(NSNotification *)notification +- (void)_bufferChangedNotification:(NSNotification *)notification { DEBUG_LOG("Document changed, reset evaluator stack"); [self.currentEvaluator cancelHandler]; @@ -232,9 +281,11 @@ - (void)handleKeyStroke:(XVimKeyStroke *)keyStroke onStack:(NSMutableArray *)eva [xvim appendOperationKeyStroke:[keyStroke xvimString]]; // Evaluate key stroke - XVimEvaluator* currentEvaluator = [evaluatorStack lastObject]; + XVimEvaluator* currentEvaluator = [[evaluatorStack lastObject] retain]; currentEvaluator.window = self; - XVimEvaluator* nextEvaluator = [currentEvaluator eval:keyStroke]; + + XVimEvaluator* nextEvaluator = [currentEvaluator eval:keyStroke]; + [currentEvaluator release]; // Manipulate evaluator stack while(YES){ @@ -289,7 +340,8 @@ - (void)handleKeyStroke:(XVimKeyStroke *)keyStroke onStack:(NSMutableArray *)eva - (void)syncEvaluatorStack { - BOOL needsVisual = (self.sourceView.selectedRange.length != 0); + XVimView *xview = self.currentView; + BOOL needsVisual = (xview.textView.selectedRange.length != 0); if (!needsVisual && [self.currentEvaluator isKindOfClass:[XVimInsertEvaluator class]]) { return; @@ -303,36 +355,22 @@ - (void)syncEvaluatorStack // FIXME:JAS this doesn't work if v is remaped (yeah I know it's silly but...) [self handleOneXVimString:@"v"]; } else { - [self.sourceView xvim_adjustCursorPosition]; + [xview adjustCursorPosition]; } [_commandLine setModeString:[self.currentEvaluator.modeString stringByAppendingString:_staticString]]; } #pragma mark - Visual gimmicks -- (NSRect)drawInsertionPointInRect:(NSRect)rect color:(NSColor*)color +- (void)drawInsertionPointInRect:(NSRect)rect color:(NSColor*)color { XVimEvaluator *current = self.currentEvaluator; - float heightRatio = [current insertionPointHeightRatio]; - float widthRatio = [current insertionPointWidthRatio]; - float alphaRatio = [current insertionPointAlphaRatio]; - - NSTextView *sourceView = [self sourceView]; - NSUInteger glyphIndex = [sourceView insertionPoint]; - NSRect glyphRect = [sourceView xvim_boundingRectForGlyphIndex:glyphIndex]; - - [[color colorWithAlphaComponent:alphaRatio] set]; - rect.size.width = rect.size.height/2; - if (glyphRect.size.width > 0 && glyphRect.size.width < rect.size.width) { - rect.size.width = glyphRect.size.width; - } - - rect.origin.y += (1 - heightRatio) * rect.size.height; - rect.size.height *= heightRatio; - rect.size.width *= widthRatio; + CGFloat heightRatio = [current insertionPointHeightRatio]; + CGFloat widthRatio = [current insertionPointWidthRatio]; + CGFloat alphaRatio = [current insertionPointAlphaRatio]; - NSRectFillUsingOperation(rect, NSCompositeSourceOver); - return rect; + [self.currentView drawInsertionPointInRect:rect color:color + heightRatio:heightRatio widthRatio:widthRatio alpha:alphaRatio]; } - (void)errorMessage:(NSString *)message ringBell:(BOOL)ringBell diff --git a/XVim/XVimYankEvaluator.h b/XVim/XVimYankEvaluator.h index 957e4142..991b6d21 100644 --- a/XVim/XVimYankEvaluator.h +++ b/XVim/XVimYankEvaluator.h @@ -7,7 +7,6 @@ // #import "XVimOperatorEvaluator.h" -#import "XVimTextViewProtocol.h" @interface XVimYankEvaluator : XVimOperatorEvaluator - (XVimEvaluator*)y; diff --git a/XVim/XVimYankEvaluator.m b/XVim/XVimYankEvaluator.m index 199a230e..2fba88fc 100644 --- a/XVim/XVimYankEvaluator.m +++ b/XVim/XVimYankEvaluator.m @@ -19,7 +19,7 @@ - (XVimEvaluator*)y{ if ([self numericArg] < 1) return nil; - XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOTION_OPTION_NONE, [self numericArg]-1); + XVimMotion* m = XVIM_MAKE_MOTION(MOTION_LINE_FORWARD, LINEWISE, MOPT_NONE, [self numericArg]-1); return [self _motionFixed:m]; } @@ -29,7 +29,7 @@ - (XVimEvaluator*)UNDERSCORE{ } - (XVimEvaluator*)motionFixed:(XVimMotion *)motion{ - [[self sourceView] xvim_yank:motion]; + [self.currentView doYank:motion]; return nil; } @end diff --git a/XVim/XVimZEvaluator.m b/XVim/XVimZEvaluator.m index c55c3f44..16af8dab 100644 --- a/XVim/XVimZEvaluator.m +++ b/XVim/XVimZEvaluator.m @@ -13,32 +13,32 @@ @implementation XVimZEvaluator - (XVimEvaluator*)b{ - [self.sourceView xvim_scrollBottom:([self numericMode]?[self numericArg]:0) firstNonblank:NO]; + [self.currentView scrollBottom:([self numericMode]?[self numericArg]:0) firstNonblank:NO]; return nil; } - (XVimEvaluator*)t{ - [self.sourceView xvim_scrollTop:([self numericMode]?[self numericArg]:0) firstNonblank:NO]; + [self.currentView scrollTop:([self numericMode]?[self numericArg]:0) firstNonblank:NO]; return nil; } - (XVimEvaluator*)z{ - [self.sourceView xvim_scrollCenter:([self numericMode]?[self numericArg]:0) firstNonblank:NO]; + [self.currentView scrollCenter:([self numericMode]?[self numericArg]:0) firstNonblank:NO]; return nil; } - (XVimEvaluator*)MINUS{ - [self.sourceView xvim_scrollBottom:([self numericMode]?[self numericArg]:0) firstNonblank:YES]; + [self.currentView scrollBottom:([self numericMode]?[self numericArg]:0) firstNonblank:YES]; return nil; } - (XVimEvaluator*)DOT{ - [self.sourceView xvim_scrollCenter:([self numericMode]?[self numericArg]:0) firstNonblank:YES]; + [self.currentView scrollCenter:([self numericMode]?[self numericArg]:0) firstNonblank:YES]; return nil; } - (XVimEvaluator*)CR{ - [self.sourceView xvim_scrollTop:([self numericMode]?[self numericArg]:0) firstNonblank:YES]; + [self.currentView scrollTop:([self numericMode]?[self numericArg]:0) firstNonblank:YES]; return nil; }