#TIL #vim goto file, goto definition

g d 키는 로컬 정의(local definition)로 이동하고 g D 키는 글로벌 정의로 이동한다. 소스 코드에서 많이 사용한다. g d 키를 눌러서 정의를 보고 Ctrl o 키를 눌러서 원래 자리로 온다.

g f 키는 파일 이름처럼 생긴 문자열이 커서에 있으면 파일을 연다. g F 키는 더 친절하다. eval.c:10 식으로 라인 번호가 있으면 라인까지 데려다준다.

참고

#TIL #vim rain 위에서 * 키는 rainbow를 찾지 못한다

/ddiary/assets/2018-01-31-til-vim-search-unbounded-word-forward-00.gif

* 키는 /\<rain\> 검색 패턴으로 찾는다. \<, \> 패턴으로 감쌌기 때문에 단어가 일치해야 한다. 반면 g * 키는 /rain 검색 패턴으로 찾는다. 단어 일치를 검사하지 않는다.

참고 - Search patterns - vim.wikia.com

#TIL #macOS 스크린샷 찍는 단축키

Command(⌘)-Shift-3

전체 화면 찍기.

Command(⌘)-Shift-4

영역을 선택할 수 있다.

Command(⌘)-Shift-4, 스페이스 바

이걸 이번에 알았다. 메뉴막대 혹은 윈도우를 깔끔하게 찍을 수 있다.

참고 - Mac에서 스크린샷을 찍는 방법 - support.apple.com

#TIL #elixir iex에서 모듈을 재컴파일하는 법

iex> c("lib/worker.ex")

모듈을 정의한 파일을 컴파일했는데

iex> r(Test.Worker)

그냥 모듈 이름만 넣어도 된다. iex에서 로드할 때, 파일 정보도 기록하는 모냥.

M-x alchemist-iex-reload-module

emacs를 사용한다면 alchemist를 놓칠 수 없다. 편집하던 버퍼에서 함수를 호출하면 된다.

링크 - IEx.Helpers.r/1

#TIL #elixir 생각해보니 MapSet.subset?

MapSet.size(MapSet.difference(a, b)) == 0

잠깐. 이상하잖아.

MapSet.subset?(a, b)

뭘 저렇게 복잡하게 하려고 했나? MapSet.subset?/2 함수랑 같다.

#TIL #elixir ! 문자로 끝나는 함수 이름 컨벤션

Enum.fetch/2, Enum.fetch!/2 함수의 차이점은? 에러 발생 여부다

iex> Enum.fetch([2, 4, 6], 2)
{:ok, 6}

iex> Enum.fetch([2, 4, 6], 4)
:error

:error 리턴. 튜플 리턴이 기본값.

iex> Enum.fetch!([2, 4, 6], 2)
6

iex> Enum.fetch!([2, 4, 6], 4)
** (Enum.OutOfBoundsError) out of bounds error

에러 발생. 원소값 리턴이 기본값.

느낌표 따위 함수 이름으로 얼마든지 쓸 수 있다. 에러를 일으키는 함수 이름 끝에는 ! 문자를 붙인다.

#TIL #vim #visual_studio #emacs jump forward, backword 키바인딩 통일

vim은 커서 위치를 jump list로 관리한다. `, /, n 등 커맨드로 이동할 때, 위치를 기록해 놓는다. 커서 위치를 바꾼다고 모두 저장하진 않는다. hjkl 키처럼 깨작깨작 움직이는 커맨드는 제외한다. jump list에 기록된 위치를 CTRL-I 키와 CTRL-O 키로 앞뒤로 점프할 수 있다.

:ju[mps]

jump list를 볼 수 있다.

visual studio

View.NavigateBackward, View.NavigateForward 동작을 각각 CTRL-O, CTRL-I 키로 바인딩하면 똑같이 사용할 수 있다.

emacs evil-mode

| C-i    | evil-jump-forward  |
| C-o    | evil-jump-backward |
| :jumps | evil-show-jumps    |

emacs에서는 evil-mode 패키지를 설치하면 사용할 수 있다.

#TIL #xcode #jenkins CocoaPods를 사용하니 링크 에러가

unity ios 빌드는 xcode 워크스페이스 혹은 프로젝트를 만드는 것까지 책임진다. xcode로 바이너리를 뽑아내는 건 사용자가 해야 한다.

firebase를 추가하고 난 후에 링크 에러가 생겼다. 알고 보니 CocoaPods 때문. firebase에서 사용한다.

워크스페이스가 아니라 프로젝트로 빌드했기 때문이다. CocoaPods는 디펜던시를 빌드하는 Pods 프로젝트를 추가해서 워크스페이스를 구성해준다. 워크스페이스는 visual studio의 솔루션 같은 거다. 프로젝트 상위 개념. Jenkins Xcode Plugin에서 Xcode Workspace File 값을 채워줬다.

#TIL #git 충돌(conflict) 났다. 한쪽 변경 사항을 적용하고 싶은데, ours야 theirs야?

충돌(conflict)이 발생했을 때, 한쪽 변경 사항을 적용하고 싶다. 주로 테스트 데이터 커밋이다. 코드 변경 사항과 섞여 들어가지 않게 데이터만 따로 커밋을 만든다. push 전에 걸러내면 되니깐. 중앙 저장소에 있는 커밋을 로컬로 가져왔는데, 데이터에서 충돌이 발생했다. 이걸 머지? 노노 양 많으면 토한다. 그럴 땐, 한쪽을 그냥 사용하면 된다.

$ git pull
$ git checkout feature-1
$ git rebase master

충돌이 발생했다.

$ git checkout --ours  <file>
$ git checkout --theirs  <file>

둘 중 어떤 걸 사용하면 되나? 난 이거 할 때마다 헷갈린다. 왜냐면 feature-1은 내가 작업한 거니깐 이게 ours 처럼 느껴지기 때문이다.

하지만 땡! rebase를 할 때, master가 부모고 feature-1 브랜치의 커밋을 하나씩 자식으로 연결한다. 즉 master가 ours고 feature-1이 theirs가 된다.

정리한다고 했지만, 막상 사용할 때가 되면 또 헷갈린다. rebase –continue 명령을 하기 전에 확인하는 습관만 들이면 좀 헷갈려도 괜찮다. 확인하고 진행하니깐.

참고

#TIL #git head 커밋 아이디?

$ cat .git/HEAD
ref: refs/heads/master
$ cat .git/refs/heads/master
246e5b84c1492682434d22448a22273b60e958aa

재미로 한두 번 이렇게 찾지 매번 이렇게 찾아야 하나?

$ git rev-parse HEAD
246e5b84c1492682434d22448a22273b60e958aa
$ git rev-parse --short HEAD
246e5b8
$ git rev-parse --short HEAD~1
42389d9

rev-parse 명령을 사용하면 된다.

참고 - https://git-scm.com/docs/git-rev-parse

#TIL #git gitignore에 / 문자가 앞에 있고 없고

$ cat .gitignore
*.c
$ git status --ignored --short
!! main.c
!! test/main_test.c

/ 문자가 없다. 모든 디렉터리를 대상으로 glob 패턴에 일치하는 파일을 무시한다.

$ cat .gitignore
/*.c
$ git status --ignored --short
?? test/main_test.c
!! main.c

/ 문자가 있다. 그것도 제일 앞에. pathname 시작과 일치해야 한다. 그 뒤에 따라오는 * 문자는 / 문자 같은 경로 구분자(path separator)와는 매치가 안 된다. 그래서 main.c 파일은 매치되고 test/main_test.c 파일은 매치 안 된다. 즉, 매치되는 main.c 파일만 무시된다.

참고 - https://git-scm.com/docs/gitignore

#TIL #elixir 컴파일 경고를 에러로 취급

def project do
  [
    # ...
    aliases: aliases(),
  ]
end

defp aliases do
  [
    "compile": ["compile --warnings-as-errors"]
  ]
end

커맨드라인 옵션은 찾았다. 디폴트 옵션이 있으면 좋겠다. 바로 실행하거나 테스트를 할 때, 코드가 변경되면 컴파일부터 한다. 이때에도 경고를 에러로 취급하는 옵션을 적용하고 싶다. 컴파일만 하면 경고가 잘 보이지만 실행하거나 테스트를 하면 빠르게 올라가서 경고 메시지를 못 볼 때가 있기 때문이다.

디폴트 옵션은 못 찾았다. 대신 alias를 사용한 방법을 찾았다. 똑똑한 방법이다.

#TIL #git 리모트 브랜치 조회

$ git fetch

가져온다.

$ git branch -r
  origin/feature-1234

리모트 브랜치만 조회. -v 옵션을 추가하면 커밋 메시지도 같이 볼 수 있다.

$ git branch -a
* feature-1234
  master
  remotes/origin/feature-1234

모두 조회. 마찬가지로 -v 옵션 사용할 수 있다.

$ git branch -vv
* feature-1234 1234567890 [origin/feature-1234] commit message 1
  master       123456789a [origin/master      ] commit message 2

트래킹 정보를 출력하려면 -vv 옵션을 사용한다.

$ git remote show origin

따끈따끈한 정보를 바로 조회할 수도 있다. 그냥 구경만 하는 게 아니고 뭔가 하려면 결국 git fetch 명령을 해야 한다.

참고

#TIL #batch #bash 제어 연산자로 만드는 명령어 리스트

제어 연산자(control operator)를 사용하면 한 줄에 여러 명령을 실행할 수 있다. 간단히 if 문(statement)을 쓴 효과를 누릴 수 있다.

command1 ; command2
command2 & command2

첫 번째 명령 성공 여부와 상관없이 연속으로 실행. 이것만 bash와 batch 스크립트가 서로 다르다. bash 스크립트는 ; 문자 사용. batch 스크립트는 & 문자 사용. 나머지는 똑같다.

command1 && command2

command1 성공하면 command2 실행.

command1 || command2

command1 실패하면 command2 실행.

short-circuit evaluation으로 이해하면 된다. exit code가 0이면 true인 게 좀 헷갈리겠지만.

cmd.exe /k ""%ConEmuBaseDir%\CmdInit.cmd" & "%ProgramFiles%\Redis\redis-cli.exe""

conemu 앱에서 redis 클라이언트 띄울 때, 사용했다.

참고

#TIL #app conemu로 git bash와 cmd를 한 화면에

/ddiary/assets/2017-11-08-til-app-conemu-show-git-bash-and-cmd-in-one-screen.jpg

> "C:\git-sdk-64\git-cmd.exe" --no-cd --command=usr/bin/bash.exe -l -i
"C:\git-sdk-64\git-cmd.exe" --no-cd --command=usr/bin/bash.exe -l -i -new_console:s
cmd.exe /k ""%ConEmuBaseDir%\CmdInit.cmd" & "%ProgramFiles%\Redis\redis-cli.exe"" -new_console:s1TVn

Startup > Tasks > Predefined tasks 메뉴로 하나 새로 등록. 그냥 emacs로 띄우려다가 탭이 너무 많아서 개발 로컬 서버는 conemu를 사용하고 있다.

홈페이지 - http://conemu.github.io/

#TIL #elixir 비어있는 map 패턴 매칭

defmodule MapUtil do
  def empty?(%{}), do: true
  def empty?(map), do: false
end
iex> MapUtil.empty?(%{hello: "elixir"})
true

기대했던 것과 달리 true가 나온다. %{} 변수에는 비어있는 map이 아니라 모든 map이 패턴 매칭된다.

def empty?(map) when map == %{}, do: true

빈 map과 같은지 검사하는 가드 절(guard clause)을 추가하거나

def empty?(map) when map_size(map) == 0, do: true

가드 절에서 호출할 수 있는 함수를 사용해서 검사하면 된다. 호출할 수 있는 함수는 제한되어 있다.

리스트도 그런가 테스트를 해봤는데, 비어있는 list에만 패턴 매칭되네. 순서가 있고 없고의 차이인가? 외우라면 외우겠지만 아직 왜 저렇게 설계했는지는 이유를 잘 모르겠다.

참고 - Pattern match function against empty map - stackoverflow.com

#TIL #elixir keyword list를 map으로 변환

iex> [key1: "value1", key2: "value2"] |> Enum.into(%{})
%{key1: "value1", key2: "value2"}

Enum.into/2 함수를 사용하면 된다. Enum.map_reduce/3 함수를 써서 복잡하게 난리부르스를 추다가 구글링으로 간편한 방법을 알아냈다.

iex> [{:key1, "value1"}, {:key2, "value2"}] == [key1: "value1", key2: "value2"]
true

special syntax를 써서 편하게 keyword list를 만들 수 있다. 실제론 tuple의 list. Enum.into/2 함수는 원소를 하나씩 변환하는데, tuple의 원소 개수가 2개면 key와 value로 해석해서 map에 추가한다.

참고 - Elixir: How to convert a keyword list to a map? - stackoverflow.com

#TIL #git 리모트 브랜치 체크 아웃

$ git checkout --track origin/serverfix

--track 옵션을 사용하면 된다. 로컬 브랜치 이름은 리모트 브랜치 이름에서 만들어준다.

$ git checkout -b sf origin/serverfix

이름을 다르게 하고 싶거나 --track 옵션을 사용했을 때, git이 이름 추정을 못 해서 체크아웃을 실패할 때, 사용한다.

참고 - 3.5 Git 브랜치 - 리모트 브랜치 - Pro Git

#TIL #vim 중괄호(braces)를 넘어 다니자

1 { 2 { 3 { |4 } A } B }

[{ 입력

1 { 2 { 3 |{ 4 } A } B }

[{ 입력

1 { 2 |{ 3 { 4 } A } B }

대괄호(square brackets)는 방향이다. 즉 [{ 키를 입력하면 이전 { 문자로 이동한다.

1 { 2 |{ 3 { 4 } A } B }

그럼 여기서 ]} 키를 입력하면 어떻게 되나?

1 { 2 { 3 { 4 |} A } B }

여기가 아니라

1 { 2 { 3 { 4 } A |} B }

여기로 입력한다. 왜냐면 unmatched 문자를 찾아서 이동하기 때문이다. {} 쌍은 무시한다.

이게 {} 문자를 사용하는 프로그래밍 언어에서는 상위 level brace로 이동하거나 현재 level의 닫거나 여는 brace로 이동할 수 있다. 하위 level은 안 된다. 왜냐면 unmatched 문자를 찾기 때문에 {} 쌍은 무시한다.

if (a == 0)
{
    if (b == 0)
    {
        if (c == 0)
        {
            |call_body();
        }
    }
}

[{ 입력

if (a == 0)
{
    if (b == 0)
    {
        if (c == 0)
        |{
            call_body();
        }
    }
}

[{ 입력

if (a == 0)
{
    if (b == 0)
    |{
        if (c == 0)
        {
            call_body();
        }
    }
}

]} 입력

if (a == 0)
{
    if (b == 0)
    {
        if (c == 0)
        {
            call_body();
        }
    |}
}

참고 - various-motions - vimdoc

#TIL #linux 왜 cannot set LC_CTYPE locale?

svn: warning: cannot set LC_CTYPE locale
svn: warning: environment variable LANG is KOR
svn: warning: please check that your locale name is correct

msys2에서 svn 명령어를 입력하니 이런 warning이 나온다. 왜?

$ locale
LANG=KOR
LC_CTYPE="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_COLLATE="C"
LC_MONETARY="C"
LC_MESSAGES="C"
LC_ALL=

locale 이름이 이상해서 발생한 것 같다. 한글 메시지는 폰트 때문에 너무 작게 나오니 영문으로 변경.

$ export LANG=en_US.UTF-8
$ export LC_ALL=en_US.UTF-8

.bashrc 파일에 추가.

$ locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_ALL=en_US.UTF-8

끝.