Estou modificando algum código elisp do linum.el:
(custom-set-variables '(linum-format 'dynamic))
(defadvice linum-update-window (around linum-dynamic activate)
(let* ((w (length (number-to-string
(+ (count-lines (point-min) (point-max)) 1))))
(linum-format (concat " %" (number-to-string w) "d ")))
ad-do-it))
Consegui consertar um erro em que o recuo era diferente, modificando (count-lines (point-min) (point-max))
para (+ (count-lines (point-min) (point-max)) 1)
. Essa foi fácil.
Mas agora eu quero modificá-lo para que a largura mínima seja 2 adicionando um if-condicional em que (concat " %" (number-to-string w) "2d ")
se a contagem de números de linha for <10.
Isso deve ser fácil! Adicione um condicional e copie / cole o concat. Pedaço de bolo, certo? Quero dizer, eu sei o que devo fazer, mas raramente toquei em elisp e sempre fico assustada quando tenho que modificar qualquer coisa com muitos parênteses.
O estilo "correto", pelo que entendi, é estruturar o código com base na indentação e agrupar os parênteses no final de uma linha, e não por si só. Vindo de outras linguagens no estilo 'C', luto tanto com a leitura quanto com a escrita de código dessa maneira. Então, minha pergunta é: o que estou fazendo de errado? Como devo editar o elisp e navegar pelo código para não precisar ficar sentado e contar todos os parênteses?
Quando trabalho com algo no elisp que fica muito profundo, tenho que fechar a porta, fechar as persianas e começar a posicionar o parêntese no estilo K&R, para que eu possa não apenas ler, mas modificar a coisa danada sem pirar.
Obviamente, estou fazendo tudo errado. Como posso tocar elisp assim sem medo?
Observe que minha pergunta é como navegar e editar o elisp não como uma questão de estilo. Já estou usando o seguinte como guia de estilo: https://github.com/bbatsov/emacs-lisp-style-guide
Atualizações:
Como formatar corretamente o elisp antes de se embaraçar no emacs.stackexchange:
Marque seu elisp e execute M-x indent-region
.
A solução do problema:
Para aqueles que querem saber como executar uma justificação correta para linum com uma largura mínima de dois, aqui está a solução:
(defadvice linum-update-window (around linum-dynamic activate)
(let* ((w (length (number-to-string
(+ (count-lines (point-min) (point-max)) 1))))
(linum-format (if (> w 1)
(concat " %" (number-to-string w) "d ")
" %2d ")))
ad-do-it))
fonte
%2d
porque, uma vez que a largura passa para 3 ou mais caracteres, ela passa de justificado à direita para justificado à esquerda.highlight-parentheses
;rainbow-delimiters
; etc. Aqui está minha própria versão simplificadahighlight-parentheses
que permite rolar sem remover os parênteses que foram coloridos pela última vez: stackoverflow.com/a/23998965/2112489 No futuro, é uma pergunta por cliente / segmento.Respostas:
Existem vários pacotes de complementos que podem ajudar, como paredit, smartparens e lispy. Esses pacotes facilitam a navegação e a manipulação do código lisp para que você possa pensar em expressões-s e deixar o editor se preocupar com o equilíbrio de parênteses.
O Emacs também possui muitos comandos internos para lidar com sexps e listas que valem a pena aprender e podem ajudá-lo a se acostumar com a estrutura do código lisp. Geralmente, eles são vinculados a um
C-M-
prefixo, como:C-M-f
/C-M-b
avançar / retroceder por sexpC-M-n
/C-M-p
para avançar / retroceder por listaC-M-u
/C-M-d
para subir / descer um nívelC-M-t
para trocar os dois sexps em torno do pontoC-M-k
matar um sexpC-M-SPC
para marcar um sexpC-M-q
reentrar um sexpO código de exemplo que você forneceu pode ser mais claro se você corrigir o recuo: coloque um ponto no início do
(defadvice...
e pressioneC-M-q
para re-recuar toda a expressão. Para substituir o(concat...
eu começaria colocando um ponto no início desse sexp e pressionandoC-M-o
para dividir a linha, preservando o recuo. Em seguida, adicione seu(if...
e use os comandos acima para ir para o final de uma lista e adicionar outro ponto de fechamento, de volta ao início para recuar, etc.Você também pode querer ativar
show-paren-mode
, o que destacará o ponto correspondente quando o ponto estiver no início ou no final de uma lista. Você pode preferir destacar a expressão inteira em vez do parêntese correspondente, portanto, tente personalizarshow-paren-style
.Como o @Tyler mencionou nos comentários de outra resposta, dê uma olhada no manual para aprender mais sobre esses e outros comandos relacionados.
fonte
C-M-q
pode ser chamado com um argumento de prefixo (C-u C-M-q
) para imprimir a expressão no ponto. Isso interromperá tudo para separar linhas e recuar, de modo que o aninhamento seja muito óbvio. Geralmente, você não quer que seu código lisp seja assim, mas pode ser útil entender um pouco de código sem inserir manualmente K&R completo nele. (E você sempre podeC-x u
desfazer).Uma boa maneira de editar o LISP é manipulando o AST, em vez dos caracteres ou linhas individuais. Eu tenho feito isso com o meu pacote lispy que eu comecei há 2 anos. Eu acho que realmente é necessário um pouco de prática para aprender como fazer isso bem, mas mesmo usando apenas o básico já deve ajudá-lo.
Eu posso explicar como eu faria essa alteração:
Passo 1
A posição inicial é geralmente ter o ponto antes do sexp de nível superior. Aqui
|
está o ponto.Você deseja que o código seja recuado corretamente, então pressione iuma vez. Ele irá reentrá-lo bem.
Passo 2
Você deseja marcar
"d "
com uma região, pois um objeto passível de manipulaçãolispy
é uma lista, que não precisa ser marcada, ou uma sequência de símbolos ou / e listas que precisam ser marcadas com uma região.Pressione atpara fazer isso. O acomando permite marcar qualquer símbolo único na lista pai e té o índice desse símbolo específico.
etapa 3
()
, pressione (.if
.()
.> w 10
.Passo 4
Para clonar a sequência:
Se você deseja desativar a região de maneira sofisticada e colocar o ponto no início da string, pressione idm:
Ou você poderia fazer m C-b C-b C-bneste caso, ou C-g C-b C-b C-b. Eu acho que idmé melhor, pois é o mesmo, independentemente do comprimento da corda. Eu acho C-x C-x C-f C-g que também funcionaria independentemente do comprimento da string.
Por fim, insira
2
para obter o código final:Sumário
A sequência completa foi de: at( C-f,
if
, (,> w 10
, C-f C-m M-m cidm,2
.Observe que, se você contar a inserção do código, as únicas chaves não relacionadas à manipulação do AST serão duas C-fe uma C-m.
fonte
Você se acostumará com o tempo, mas é claro que há muito o que fazer para ajudar a acelerar:
Indentação
Existe um método para essa loucura. No lisp você pode fazer isso:
Suponha que essa
1
seja realmente uma expressão grande e que você queira recuá-la em sua própria linha.Isso é enganador.
1
é recuado apenas em um slot, apesar de serem três níveis inteiros de aninhamento mais alto! Melhor colocá-lo por seus pais.Se usarmos esse esquema em seu próprio código, obtemos o seguinte:
Observe a forte correlação entre o recuo e o nível de aninhamento. Linhas com um nível maior de aninhamento sempre são recuadas mais do que linhas com menos.
Isso por si só vai ajudar muito. Você pode ver facilmente a "forma" do código, e a maioria dos outros idiomas o treinou para associar a indentação com blocos e blocos com alguma forma de aninhamento (explícita ou implícita).
Como exercício, removi os parênteses não essenciais do código. Você deve ler o código e fornecer os parênteses ausentes, considerando o conhecimento básico do Elisp (a saber, quais são as funções e valores e a estrutura de
let*
).fonte
newline-and-indent
para emacs-lisp-mode e lisp-interação-mode resolva seu primeiro exemplo, PythonNut. E o TAB fará o trabalho o resto do tempo.Estou incluindo isso como uma resposta diferente.
Às vezes, o recuo irá falhar. Seu recurso, então, é usar algo como
rainbow-delimiters
:Isso torna o nível de aninhamento de qualquer parêntese explícito e facilmente verificado.
Porém, há um grande problema: os parênteses são ridiculamente perturbadores. Há um equilíbrio de ênfase aqui.
rainbow-delimiters
normalmente colocará muita ênfase nos parâmetros às custas do restante do seu código! Mas se você tonificar o arco-íris voltado para baixo, eles se tornarão progressivamente mais difíceis de digitalizar.Se você pode encontrar um conjunto de faces que equilibra isso bem, use-o de qualquer maneira.
Dito isto, uma solução (a que me faz rir de alegria) é ter dois pares de rostos e alternar entre eles rapidamente. Quando você faz outras coisas, os rostos são diluídos e desbotam em segundo plano:
Mas quando você coloca o ponto em um parêntese, ele perfura os parênteses para que você possa ver os níveis de aninhamento:
E aqui está o código para fazer isso:
fonte
Se você recuar seu código, precisará realmente recortá-lo corretamente. Os desenvolvedores do Lisp têm expectativas de como deve ser o recuo adequado. Isso não significa que o código tenha a mesma aparência. Ainda existem maneiras diferentes de formatar o código.
Eu esperaria que o recuo mostre contenção de alguma maneira. Mas não no seu código.
Por padrão, seu código pode ser recuado dessa maneira. Outras respostas descreveram como você pode fazer isso com a ajuda do Emacs:
Eu esperaria que as
let
variáveis ligadas iniciassem na mesma coluna vertical. Eu também esperaria que o corpo dolet
é menos recuado. Aprendemos como alet
deve ser recuado. Depois de treinar um pouco, é um padrão visual fácil de reconhecer.Os principais componentes visuais do código são o
defadvice
,let*
e várias chamadas de função. Odef
no nome me diz que é uma definição seguida por um nome, uma lista de argumentos e um corpo.Então eu gosto de ver
o
let*
serialet*
:, uma lista de ligações e um corpo.Assim, eu gosto de ver:
Mais detalhado:
Para uma chamada de função, gosto de ver:
ou
ou
Qual deles é usado depende de quanto tempo os argumentos são. Não queremos fazer linhas muito longas e cada argumento em sua própria linha nos permite ter mais estrutura.
Portanto, você precisa escrever seu código usando esses padrões e garantir que o leitor possa identificar facilmente esses padrões no seu código.
Os parênteses devem incluir diretamente suas construções.
Evite exemplos de espaço em branco:
ou
ou
No Lisp, os parênteses não têm função sintática da linguagem, eles fazem parte da sintaxe da estrutura de dados da lista (expressões-s). Assim, escrevemos listas e formatamos as listas. Para formatar o conteúdo da lista, usamos conhecimento sobre a linguagem de programação Lisp. UMA
let
expressão deve ser formatada diferente de uma chamada de função. Ainda ambas são listas e os parênteses devem incluir as listas diretamente. Existem alguns exemplos em que faz sentido ter um único parêntese em uma linha, mas use isso raramente.Use os parênteses para ajudá-lo a encontrar os limites da expressão. Deixe-os ajudá-lo a mover-se de lista em lista, editar listas, cortar e copiar listas, substituir listas, recuar / formatar listas, selecionar listas, ...
fonte
Para obter ajuda básica com indentação, use TAB e "CMq" (
indent-pp-sexp
); a ligação "Cm" paranewline-and-indent
evitará que você pressione TAB após uma nova linha.Você pode navegar pelo sexp com "CM-esquerda / direita / cima / baixo / casa / fim", o que é muito útil.
Se você estiver preocupado com a manutenção do equilíbrio entre parênteses, use algo como smartparens (é um pouco mais perdoador que paritar , que pode inicialmente parecer usar uma camisa de força). Ele também vem com muitas ligações para navegar e editar no nível sexp.
Por fim, para realçar parênteses, comece ativando a opção (menu Opções-> realçar parênteses correspondentes ou
show-paren-mode
.) "fonte
Como complemento ao que outros deram como respostas, aqui estão meus 2 centavos:
emacs-lisp-mode
é essencial.blink-matching-paren
está ativado (nãonil
) por padrão. Deixe assim.show-paren-mode
para destacar o parêntese esquerdo correspondente ao parêntese direito antes do cursor.Eu não utilizar uma conexão elétrica ou arco-íris qualquer coisa ou qualquer outro tal "ajuda". Para mim, isso é um incômodo, não um auxílio.
Em particular,
electric-pair-mode
é, para mim, um incômodo inconveniente. Mas algumas pessoas adoram. E imagino que possa ser útil em outros contextos de emparelhamento além dos parênteses do Lisp (imagino que sim, mas também não estou convencido de tais casos de uso).Você encontrará o que funciona melhor para você. As principais coisas que quero dizer são: (1) o recuo automático é seu amigo, como é uma maneira de identificar o parêntese esquerdo que corresponde ao parêntese direito antes do ponto.
Em particular, ver o que o recuo automático faz por você ensina imediatamente que você nunca mais deseja colocar os parênteses em uma linha sozinhos. Esse estilo de codificação, que é onipresente em outros idiomas, é apenas barulhento.
Além do recuo automático que você utiliza
RET
eTAB
se sente confortável usandoC-M-q
. (UseC-h k
ememacs-lisp-mode
descobrir o que essas chaves fazer.)fonte
Notei que algumas pessoas já mencionaram delimitadores de arco-íris, esse também é o meu modo favorito ao editar o código lisp.
Na verdade, eu amo parênteses, a melhor coisa sobre o Lisp são esses parênteses, é divertido lidar com eles se você souber como:
instale o modo evil e seu plug-in evil-surround, para que você possa editar parênteses facilmente, por exemplo, pressione
ci(
removerá tudo o que estiver dentro()
,va(
selecionará a coisa envolvida por "()" e "()". e você pode pressionar%
para pular entre os parênteses correspondentesuse flycheck ou flymake, parênteses incomparáveis serão sublinhados em tempo real
fonte
A dificuldade aqui é que você deseja adicionar algum código antes e depois de um certo
sexp
. Nesta situação, o sexp é(concat " %" (number-to-string w) "d ")
. Você deseja inserir a instrução if(if (> w 1)
antes e a instrução else" %2d ")
depois.Uma grande parte da dificuldade é isolar isso
sexp
, pessoalmente eu uso o comando abaixo para isolar umsexp
Basta colocar o cursor no início de
(concat " %" (number-to-string w) "d ")
, ou seja, no primeiro(
, e depois aplicar o acimaisolate-sexp
. Você obtémEm seguida, adicione o código apropriado antes e depois disso
sexp
, ou seja,E pronto. O código obtido dessa maneira talvez não seja bonito, mas a chance de se perder é menor.
fonte