퇴근5분전

 공통 UI쪽 관련해서 조금 만지는 부분이 있는데... 

코드를 아래와 같이 쓸수 있으면 어떨까? 라는 생각을 해봤다. 

  Call()( msg => msg( "x" ) );

 

Call() 호출하고 이어서 대리자로 연결된 메서드에 파라미터를 받아서 전달!!

 

UI를 다루면서 두가지 처리를 연속적으로 하게 되는 거였다. 

UseFlowBodyPanel();

foreach( var data in ListDatas ) {

     AddFlowBodyItem( data );

}

이런 식으로 처리하는 걸... 줄여서 

UseFlowBodyPanel()( AddFlowBodyItem => foreach( var data in ListDatas ) AddFlowBodyItem( data ) );

 

실제 코드는 여러가지 조건들을 고려해서 조금 바뀌지만...위 처럼 갈 것이다. 

 

다만 Call()은 테스트 코드로 작성했던것이기에... 기록으로 남겨두려 한다. 

 private Action<Action<Action<string>>> Call()

        {

            System.Diagnostics.Debug.WriteLine("Call!");

            Action<Action<Action<string>>> returnMethodCall

              = new Action<Action<Action<string>>>((msg)=> msg( Call_After ));            

            return returnMethodCall;

        }

 

        private void Call_After(string msg)

        {

            System.Diagnostics.Debug.WriteLine("Call_After!" + msg);

        }

 

Action<string> 은 Call_After( string msg )와 연결되는 게 기본이니까 알겠는데... 

 

연결지으려면 왜 Action<Action<...>> 으로 두개가 필요한건지는 잘 모르겠네... 

 

찾는 방법은 Call() (  <-- 이렇게 코딩해놓고 vs에 표시되는 인자값이 Action<string> 인지 확인하였더니 

Action<Action<...>> 이었다. 

 

Action  mth = s => console.Write( s ); 이런 형태로 Action이 빠지니까.. 

Call() 에서 리턴받은 object가 Action< Action < Action<string> > >

인데... 

Action이 하나 빠지고 

msg => msg( "x" ) 

msg는 Action<string> 이니까... 

Action<Action<string>> 을 정의한거고... 

.... 뭐래는겨......... 모르겠다 ㅠㅠ. 

머리속에 맞춰지지 않는 퍼즐이 있는 것 같은 느낌... 아.. 싫어.. 

 

Action<Action<string>> mth1 = mthCall => mthCall("?");
mth1(Call_After);

---------------------------------------------------------------------------------

Action대신 Method로 바꿔 생각해보면... 

 

Action< Action< Action<string > > >
Mth0(   Mth1(    Mth( string ) ) )
Mth0(   Mth1(    MethodName    ) )

Call()(  MethodName => MethodName( prms ) );

Action<string>  
   mth( string => string )

Action< Action<string > >
   mth1(   MethodName => MethodName( str )  )

Action< Action< Action<string > > >
   mth0(   mm =>  mm( MethodName ) )

 

Action<Action<Action<string>>> returnMethodCall

              = new Action<Action<Action<string>>>((msg)=> msg( Call_After ));            

 ----->    (msg)=> msg( Call_After )

 

이거 때문에 잠도 못잤네. 

Call() (   여기에서 왜? mth0이 사라지고 mth1이 대입되는거지??? ) : 두개가 벗겨지네...

 

 

Action<Action<Action<string>>> returnMethodCall = Call();
returnMethodCall( msg => msg("???") );

음... 실제 리턴받은걸 실행해보면... 위처럼 되네...? 

어라... 뭐지? 

 

Action<Action<Action<string>>> returnMethodCall

              = new Action<Action<Action<string>>>((msg)=> msg( Call_After ));     

 

returnMethodCall(   이자리는  Action<Action<string>>  타입이 들어와야 하니.. m => m("?") ); 

아... 이랬던가? 

new로 대상을 지정하는 람다식과 대리자를 호출하는 부분에 차이때문에 내가 헷갈린건가... 

아무렇지도 않게 사용하던거에... 정신이 나가버릴것 같네... 이제 이해가 된다... 

 간만에... 이 프로그램을 수정하였다. 

회사에서 출근해서...

 1. 사이트 로그인을 할때 크롬켜서 사이트 이동.

 2. 비번 프로그램 켜고... 비번 넣고.

 3. 사이트 찾아서... GEN 클릭

위 세가지를 하게 된다. 

 

갑자기 사이트 찾아서 gen누르기가 귀찮아졌어 ㅠㅠ... 

 > 물론, 크롬에서 지원하는 id/pwd 저장기능 쓰면 더 편하긴 하겠지만... 

 > 내가 만든 프로그램을 써야 한다... 그래야 업그레이드도 하지... 

 

개선사항

  - 바로가기 만드는 기능을 추가한다.

  - 바로가기 실행을 하면

  - 비번 프로그램이 켜지고 비번 넣으면

  - 지정된 사이트 비번을 클립보드에 복사

  :: 곧 위에서의 3번을 안해도 된다!

 

Link 버튼을 넣었다. 클릭하면 

바탕화면에 아이콘을 만들어준 icon은 *** 인데 바탕화면이 밤하늘 사진이라 안보인다... 

클릭 하면 

비번 창이 열리고... 비번을 넣으면...

처리 결과를 알려준다.

--- 끝 ---

 

페이지 기능을 추가 하였다. 

 

지난번 업그레이드를 한 후 요구사항에 대한 분석을 하다보니... 

쓰기가 너무 편하고 좋아서... 요구사항을 화면별로 따로 하던걸

요구사항 자체에 대한 분석을 하나로 모으고자 페이지 를 추가하였다. 

 

기존 :

         요구사항 : 화면1 - 분석

         요구사항 : 화면2 - 분석

 

현재 : 

         요구사항 : 화면1, 화면2, ... 

 

 다만 아쉬운건 기존 분석내용을 다 날렸다... 페이지 넣고 자료구조를 바꿨더니... 

좀 즉흥적으로 작업을 시작하여 기존내용을 버리게 되었다. 

 

 기존 작업한 내용들은 회사 위키에 다 올려놨으니... 

새로 작업하는건 새로운 툴에 또 ... 

 

올해 코딩은 이게 끝인가보다... POE나 해야지. 

 업그레이드 이유 : 자바 프로젝트를 진행하면서.. 

controller 호출, Servlet 호출 등을 함수연결 프로그램으로 기록하고 분석하면서 요긴하게 써먹었는데... 

1주~2주 정도 되어서 다시 해당 화면의 기능을 보려고 하니... 한눈에 안들어온다.

 화면에 어떤 요소에서 시작인지에 대한 기록이 없어 그런가 하고... 화면 캡처해서 해당 요소의 위치에서 

시작하면 어떨까 싶어서... 시작부분을  바꾸었다. 이것도 사용해봐야 개선이 된건지 안된건지 알 수 있다.

월요일에 다시 써보면서 괜찮을지 검토해봐야 한다. 

 

 추가적으로 생각해보기론...

서비스 구현된 것을 분석한다거나 화면이 없는 것들에 대하여 ppt로 만들어진 화면으로 대체하면 보기 좋지 않을까? 

 

####### 시작 ##################################

이번에 업데이트 한 화면 캡쳐해서 넣을 수 있다.

그리드 디자이너 화면을 캡쳐하여 붙여넣고... 

화살표 클릭 후 열기 버튼 아래에서 부터 마우스로 쭉 드래그 해서 놓으면 화살표가 그려진다. 

이때 컨트롤 키를 누르고 있으면 Procedure 생성창이 바로 뜬다.

컨트롤 키를 누르지 않으면 그냥 화살표만 그려준다.

Procedure버튼을 누르고 필요한 위치에 클릭하면 생성창이 뜬다.

[열기] 라는 박스를 더블클릭 하면 -- 기존에 함수 그려서 보여주던 폼이 뜬다. 

해당 폼에서 [작성하기] 버튼을 눌러 편집 화면을 띄우고... 여기에 함수관련 코드를 분석해서 넣고 저장하고 닫아주면...

이렇게 함수간 호출에 대하여 화살표로 그려준다. 또 함수안에서 처리한 일을 요약해서 기록하는 것이라서 잘 기록해두면 

나중에 인수인계나 공유하기도 좋지 않을까? 

 ( 물론 열기에 해당하는 소스를 분석한 코드로 입력한게 아니라 샘플로 작성된 것이다. )

배율 부분은 트랙바로 바꾸었다. 기존에 파일열고 저장하던 부분을 모두 지웠다.  

 

- 남은 작업은 작성할때 사용하는 인텔리센스처럼 소스 관리 파일들 목록을 읽어오는 부분을...

 기존에 하드코딩으로 되어 있던 내용을 UI로 수정해야 할 듯 하다. 

그래야 어느 사용자든지 이 프로그램으로 소스분석에 필요한 정보를 쉽게 편집기에 넣을 수 있게 된다.

화면 스샷!!

 자바를 하다보니... 기존에 FunctionConnection 으로 소스를 분석하면서 많은 도움이 되었지만... 

문제는 일주일 정도 지나서 다시 열었을때다... 

 이게 처음엔 빠르게 소스추적을 입력을 하기 위해 UI를 제대로 구성하지 않고 텍스트로 입력하는 것을 만들었지만, 

일주일 후에 다시 열어서 확인하려고 보면, 어디서 어떤 컨트롤러나 서블릿을 호출했는지 눈에 안들어오더라... 

다시 처음부터 쭉~~ 따라 갔어야 했기에... 여기에 조금만 추가해보기로 했다. 

 

  1. 화면스샷!

  2. 특정위치에 Action이나 서블릿 시작포인트 표시

  3. 해당 포인트를 가르킬 화살표!

 

 위에 이미지는 [테스트] 라는 액션 정보를 배치해놓았다. 

화살표 두개는 배치해보고... 이동도 해보고 화살표방향도 재조정해보고... 

저 [테스트] 를 더블클릭하면, 이때 FunctionConnection 프로그램으로 연결해준다. 

 

 완성되면 좀더 다듬어서 또 유툽에 올려야지.. 

  음 오늘도 열심히 시스템 소스 분석을 하다보니... 

이걸 이렇게 쓰면 좋겠다 싶어서... 

요청을 하는 index에서 서버에 요청을 하면 controller가 받아서 db데이타를 가져다 모델을 만들고 view를 반환 하면.. 

index는 받아서 view를 엘리먼트에 load() 한다... 라는 흐름은 위처럼 표시해주는게 보기 좋을듯 하여... 

-----------------------------------

fn:index, file:메인
...어떤 처리를 하고..
...메뉴중에...
//MVC에서 Controller를 호출하기 위한 클릭!
fn:requestPage, ref:Controller
받은 view를 어느 dom에 Load(html)
fn:생성, ref:View화면
#종료

-----------------------------------

fn:requestPage, file:Controller
//요청받은 파라미터로 모델을 만든다. 
fn:getModel, ref:DataBaseLayer
@모델을 받고
@return View화면!

-----------------------------------

fn:getModel, file:DataBaseLayer
//DB에서 어떤 데이타를 조회하여 
//모델을 만들어 반환한다.
!return 모델

-----------------------------------

fn:생성, file:View화면
!경고...
@주황...
#???
//주석
일반(검정)

-----------------------------------

이런식으로 작성하면... 될 듯하다.

 

 DB 를 어찌 표현해준다... DB를 별도 프로그램에 띄우면 한번에 안보이는데... 

 

 흐름을 쫒아서 view반환이 오른쪽 끝으로 가버리면 컨트롤이랑 레벨이 안맞는것 같다는 느낌이 들었다. 

소스를 추적하면서 간단히 정리하는데만 사용중이지만... 

 기능 자체를 너무 길게 그리면 보기 어려워지는 단점이??

또 한번에 하나만 보이니까... 멀티로 보는 방법을 생각하든가 아니면 LINK: 를 추가해볼까 한다. 

링크로 처리된 부분은 클릭하면 해당 링크가 팝업이나 뷰 자체를 바꿔주는 방법으로... 생각해볼까 한다. 

 

 추가 -------------------------------------------------------------------------------------------- 11-30

 

//테스트 사이트 : http://regexstorm.net/tester

 

.NET Regex Tester - Regex Storm

Home Tester Reference About

regexstorm.net

 //테스트 문자열

 // fn:함수. a!@#$%^&*(()_-+=\\<>,a a , file:파일.111/\ !@#$%^&-=~*()! \fff, ref:r파asdf일, prj:프로젝트, link:기능함


 //fn(\s)*(:|=)(\s)*(?<fn>\w+(\>|\<|\~|\+|\=|\-|\)|\(|\^|\*|\&|\%|\$|\#|\@|\!+|\\+|\/+|\s+|\.*\w*|(?=\,))*)
 //file(\s)*(:|=)(\s)*(?<file>\w+(\>|\<|\~|\+|\=|\-|\)|\(|\^|\*|\&|\%|\$|\#|\@|\!+|\\+|\/+|\s+|\.*\w*|(?=\,))*)        
 //ref(\s)*(:|=)(\s)*(?<ref>\w+(\>|\<|\~|\+|\=|\-|\)|\(|\^|\*|\&|\%|\$|\#|\@|\!+|\\+|\/+|\s+|\.*\w*|(?=\,))*)
 //proj(\s)*(:|=)(\s)*(?<proj>\w+(\>|\<|\~|\+|\=|\-|\)|\(|\^|\*|\&|\%|\$|\#|\@|\!+|\\+|\/+|\s+|\.*\w*|(?=\,))*)
 //link(\s)*(:|=)(\s)*(?<link>\w+(\>|\<|\~|\+|\=|\-|\)|\(|\^|\*|\&|\%|\$|\#|\@|\!+|\\+|\/+|\s+|\.*\w*|(?=\,))*)

 

프로젝트, 링크 등을 추가 할수 있는 정규식.

저녁에 와서 정규식 테스트만 했다. 

전에 쓰던건 fn, ref|file 만 필터링 했던 테스트 문장이었으나... 차라리 문자열에서 위 정규식 5개를 적용해서 값을

꺼내는게 나을듯 하다.  복합적으로 그룹지어서 뽑으려니까 예외상황의 문자열을 못 뽑아내더라...

위 구문도 정답은 아닌듯... 일단 써야지.. 

 

 프로젝트 별로 하위 파일목록을 관리하면 어떨까 해서... 고민중이다. 

회사에서 프로젝트만 구분해서 해봤는데... 

 

------------------------ 22년 12월 08일 추가.

 

 (\s)*(?<=[,])(\s)*  키워드 앞에 추가하였다. 전방탐색!

document.location.href : xxxxxxxxxxxx.jsp

json파일 내용 일부를 붙였더니 함수로 판단을 해서 

함수로 그려버렸다. 

키워드 앞에 콤마(,)를 탐색! 또는 공백(\s)이 있으면 포함해서 찾는다.

다른 기호들이 들어오면 패턴 불일치로 일반 컨텐츠로 판단한다. 

정상적으로 시작하는 fn도 못찾네...

추가: 색상변경.

 

색상관련해서 ... 조카의 도움을 좀 받고...

배경이나 라인 색상을 좀 바꾸고... 

주의성 문구, 파라미터, 주석, 반환타입관련 색상 수정!! 

-----------------------------------------------------------------------------------------------------------------------------------

 

 일단... 1차 개발로 아래처럼 나오도록 개발은 완료 했다. 

프로젝트 업무 파악하면서 호출 흐름을 기록할 수 있다.

 

입력편의성을 위해 'text' 문자열로만 작성하여 그 데이타를 기반으로 아래처럼 그려준다.

 

입력화면이다. 

텍스트를 작성... 마우스는 필요없고... 소스 보면서 무지성 타이핑으로... 

 

#fn:btnEdit_Click, File:/JSFW.GridDesigner/DesignForm.cs <- fn:{함수명}, file:{파일} 입력 기준
// 파일명 체크
.fn:Edit, ref:/JSFW.GridDesignerCommon/GridSettings.cs <- fn:{함수명}, ref:{파일} 입력 기준 여기서F12를 누르면!!!(1)
// 그리드 정보 초기화.
// 표시되는 그리드에 변경된 내용 적용

 

#fn:Edit,file:/JSFW.GridDesignerCommon/GridSettings.cs
.fn:ShowDialog, ref:/JSFW.GridDesignerCommon/SourceGridDll/GridEditForm.cs
// 그리드 설정을 하고.. 
// [적용]버튼을 클릭
.fn:btnApply_Click, ref:/JSFW.GridDesignerCommon/SourceGridDll/GridEditForm.cs
// 변경된 그리드 정보 적용

...

 

(1) F12를 누르면 작성된 fn: , file: 부분이 있으면 커서 이동.

      작성된 곳이 없으면 새 textBox를 만들고 거기에 fn: , file:을 만들고 커서 이동.

 

file:또는 ref: 를 입력하고 파일명을 치면 우측 리스트 박스에 해당 이름이 포함된 검색결과가 나타난다.

나타난 결과를 더블클릭하거나 [Ctrl + Enter]를 치면 바로 들어간다.

 [파일명 목록 새로고침]에 특정 경로와 파일확장자를 넣으면 대상을 검색해서 가지고 있게 된다. 

 1회만 하면 된다. 

  Dictionary<string, string> targetFolders = new Dictionary<string, string> {
                //{ "경로", "*.cs|*.xml" }
  };

 이런 형태로 등록하면 된다. 

 

입력중에 텍스트 박스에 내용을 모두 지우고 포커스를 빼면, 자동 삭제된다.

 

text 작성 중에는 별도의 마우스 조작이 있으면 안될 것 같아서 단축키를 지원하고, 

마우스 조작을 최소화 하였다. 

 

2차 업그레이드는 현재의 프로젝트가 끝나면(내년 10월 이후) 생각나면??? 

1. Link: 같은걸로 다른 저장된 함수랑 연결해서... 하이퍼 링크 같은걸 지원하면 ... 어떨까? 싶다.

2. 시퀀스 다이어그램 처럼.. 오브젝트를 일렬로 세워?... 

 

이런 형태나... 

이런 형태도... 고민해봤다... 

 

지금은...

시작 ---------> 호출 ----------> 호출 ----------> 호출 ------------> 종료 

이런 형태라서.. mvc에서 클릭요청 이벤트를 따라가면 view반환은 오른쪽에서 보인다. 

호출되는 순서대로 쭉~~ 따라가서 끝에 이르면 원래의 목적의 뷰를 반환해주는 형태로 그려지기 때문에... 

 

 

지금 그려지는 모습은

Controller의 제일 아래보면 ref=생성 >> View화면 쪽으로 화살표...를 그려준다. 

그 View화면에서도 처리 할 일들에 대한 기록을 남겨준다. javascript도 있고. 중요한 화면 이슈도 있을테니...

그리고 ref=생서 앞에는 녹색의 화살표로 원래 Controller.requestPage를 호출한 ref=requestPage쪽으로

되돌아가는 화살표를 그렸다. 

 

몇개의 기록들을 하면서 소스흐름을 따라가보니... 자유도가 높아서 괜찮은듯... 

파라미터와 반환값을 기록 하려다가? 이것도 쓰기 나름인데... 너무 자세히 기록하면 사용감이 피곤해질듯... 

그래도 필요하면!!

fn:함수, file:파일

@param1

@param2

@paramN...

#returnValue

형태로 기록 해두어도 괜찮을 것 같긴하다.

 

!, @, #, //  <-- 각각의 지정한 색상이 있다. 

 

일단 회사에서 소스분석용 데이타를 넣고 보니.. 옅은색이긴 하지만 화살표가 왔다 갔다... 정신없고

색이 너무 많은듯 한데...? 

 

전에 구현했던게 있으니.. 

https://aseuka.tistory.com/entry/%EC%BB%A8%ED%8A%B8%EB%A1%A4-%EB%B0%B0%EC%9C%A8-%EC%A1%B0%EC%A0%95-%EA%B3%B5%EC%8B%9D

 

컨트롤 배율 조정 공식...

ZoomInOut을 구현했는데... int[] factors = { 50, 100, 200 }; 을 combo에 넣고 옛날 값 = 100; 으로 초기값 사용. 현재 선택된 Factor = 50; { float 조정배율 = 현재값/ 옛날값 ; Control.Scale( new SizeF( 조정배율 , 조정배

aseuka.tistory.com

int factorIndex = 4;
float[] factors = new float[] { 0.2f, 0.4f, 0.6f, 0.8f, 1f, 1.2f, 1.4f, 1.6f, 1.8f, 2f, }; // 20 ~ 200%
float oldSizeFactor = 1f;

 

- ZoomOut

factorIndex--;
if (factorIndex < 0) factorIndex = 0;              
pnlBoard.Scale(new SizeF(  factors[factorIndex] / oldSizeFactor,   factors[factorIndex] / oldSizeFactor));

pnlBoard.Invalidate();
oldSizeFactor = factors[factorIndex];

 

// 배율 조정시 스크롤이 있는 상태일때!! 왼쪽상단(left, top)에 판넬을 맞춰줘야 하니까.. 

int hv = pnlOutBorder.HorizontalScroll.Value;
int vv = pnlOutBorder.VerticalScroll.Value;
pnlBoard.Left = -hv;
pnlBoard.Top = -vv;

 

- ZoomIn

 factorIndex++;
 if (factors.Length <= factorIndex) factorIndex = factors.Length-1;             
 pnlBoard.Scale(new SizeF(factors[factorIndex] / oldSizeFactor, factors[factorIndex] / oldSizeFactor));

 pnlBoard.Invalidate();
 oldSizeFactor = factors[factorIndex];          

 

// 배율 조정시 스크롤이 있는 상태일때!! 왼쪽상단(left, top)에 판넬을 맞춰줘야 하니까.. 

int hv = pnlOutBorder.HorizontalScroll.Value;  
int vv = pnlOutBorder.VerticalScroll.Value;
pnlBoard.Left = -hv;
pnlBoard.Top = -vv;

 

** 함수, 호출 화살표등을 직접 드로잉 처리하고 있어서

이에 대한 크기조정도 적용해줘야 한다. 

파일, 함수, 화살표등을 그리기전에... 

 e.Graphics.ScaleTransform( factors[factorIndex], factors[factorIndex] );

배율 조정을 해준다.

 

 

그리고 마우스 다운 이벤트에서 파일과 함수인경우 이름을 복사!

호출하는 함수부분에서는 대상 함수로 화살표의 색을 바꿔주기 때문에... 배율조정시 마우스 다운 위치를 계산해줘야 하는데... 

Point pt = new Point((int)((float)e.Location.X / oldSizeFactor), (int)((float)e.Location.Y / oldSizeFactor));

 

 

 제목 쓰기 어렵다.

함수연결 보기 프로그램에 파일박스 안에 나열된 함수(fn1)와 그 안에서 호출되는 외부함수(fn2)의 연결을 

호출단계(레벨)별로 추가해서 그렸다. 

연결 호출 ...

대충 단계를 표시하기 위해 간소하게 했지만... 

 

 그래도 프로젝트에서 분석하면서 써보니... 나름 괜츈!!

다만 전자정부 플젝 소스 보니 눈돌아갈것 같아. 왜 이렇게 복잡한걸까? 

 문서가 없다더랑.. 보안상?? 두분 계시는데 인간 라이브러리... 

다만... 내가 질문을 할 수 있을지... 서로 귀찮은데... 잘못들어갔나 싶기도 하고... 나올까? 

 

 얼마나 자세히 적을 것인가? 이게 문제이긴하다...

너무 자세히 쓰면 소스 따라 치는 것 처럼 나오고... 다이어그램도 너무 복잡하게 보일테니... 

주요 로직만 적는 것으로 작성자가 적절하게 맞춰야 할 것 같다. 

 

 이번 AsIs Function Connection 프로그램 구현 중... 

텍스트 입력기반으로 만들었기에... 편의기능을 추가했다. 

생각지도 않았던 Alt + Up, Alt + Down 으로 VS 편집기처럼 선택한 행들의 위치를 변경하는 것을 구현하게 되었다. 

인텔리센스를 구현하려니... 참 걸리는게 많네.. 대충 흉내만 냈다. 

 

Textbox_KeyDown Event에서!!

 // txtSource : TextBox 임 

 // !!! 주의 : 프로퍼티에 WrapWord = false; 로 작업해야 함. 

 // 이유 : textbox의 내용이 길때 textbox 크기가 작아지면서 박스안에 글이 구겨들어간다. 이때 커서의 현재 위치 정보를 제대로 읽을 수 없었다.

 

            if (e.Alt && (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down))
            { 
                int selStartIndex = txtSource.SelectionStart;
                int selLength = txtSource.SelectionLength;
                int selLineCnt = 1 + txtSource.SelectedText.Count(c => c == '\n');
                int currentlineIndex = txtSource.GetLineFromCharIndex(selStartIndex);
                List<string> lines = new List<string>(txtSource.Lines);

                if (e.KeyCode == Keys.Up && 0 <= (currentlineIndex -1 ))
                { 
                    string moveString = lines[currentlineIndex - 1]; 
                    lines.RemoveAt(currentlineIndex - 1);
                    lines.Insert(currentlineIndex + selLineCnt - 1, moveString);
                    int offset = moveString.Length;                    
                    txtSource.Text = string.Join(Environment.NewLine, lines);
                    lines.Clear();
                    txtSource.SelectionStart = selStartIndex - offset -2;
                    txtSource.SelectionLength = selLength;
                }
                else if (e.KeyCode == Keys.Down && (currentlineIndex+ selLineCnt) < lines.Count)
                {
                    string moveString = lines[currentlineIndex + selLineCnt];                    
                    lines.RemoveAt(currentlineIndex + selLineCnt);
                    lines.Insert(currentlineIndex, moveString);
                    int offset = moveString.Length;
                    txtSource.Text = string.Join(Environment.NewLine, lines);
                    lines.Clear();
                    txtSource.SelectionStart = selStartIndex + offset +2;
                    txtSource.SelectionLength = selLength;
                }
            }

 

 

구현 로직 설명