Tutorial EELisp

Aprenda EELisp construindo projetos reais. Da sua primeira expressão a uma calculadora de juros compostos, controle de despesas e um organizador pessoal inspirado no Lotus Agenda.

Conteúdo

  1. Sua Primeira Expressão Básico
  2. Variáveis & Funções Básico
  3. Listas & Funções de Ordem Superior Básico
  4. Dicionários Básico
  5. Calculadora de Juros Compostos Projeto
  6. Gerenciador de Contatos com Tabelas Projeto
  7. Controle de Despesas Projeto
  8. Construindo uma Lista de Tarefas Projeto
  9. Livro de Receitas com Navegação & Edição Projeto
  10. Formulários Calculadora com Campos Computados Projeto
  11. Agenda Pessoal: Itens & Categorias Projeto
  12. Regras de Auto-Categorização Projeto
  13. Visões — Perspectivas Dinâmicas Projeto
  14. Integração com Calendário Projeto
  15. Entrada Inteligente & HTTP Projeto
  16. Itens Recorrentes & Modelos Projeto
  17. Múltiplas Agendas Projeto
  18. Barra Lateral da Agenda & Integração com Calendário Projeto
  19. Dicas & Padrões Referência

1. Sua Primeira Expressão

Abra a barra lateral do REPL no EEditor com ⌘⇧L. Ela aparece como um painel no lado direito do editor e permanece aberta enquanto você troca de arquivo. Digite uma expressão e pressione ⌘Return para avaliá-la. Seu histórico de comandos é salvo entre sessões — use as setas para cima/baixo para recuperar comandos anteriores.

No EELisp, tudo é uma expressão envolvida em parênteses. O primeiro item é a função, o restante são os argumentos:

;; Aritmética — o operador vem primeiro
(+ 1 2)              ; → 3
(+ 1 2 3 4)          ; → 10  (variádico!)
(* 6 7)              ; → 42
(/ 100 3)            ; → 33.333...

;; Aninhamento — expressões internas são avaliadas primeiro
(+ (* 3 4) (- 10 5))  ; → 17  (12 + 5)

;; Strings
(str "Hello, " "world!")  ; → "Hello, world!"
(str-upper "hello")          ; → "HELLO"

;; Funções matemáticas
(pow 2 10)            ; → 1024
(round 3.14159 2)    ; → 3.14  (arredonda para N casas decimais)
(abs -42)             ; → 42

;; Comparações retornam true ou false
(> 10 5)              ; → true
(= 1 1)              ; → true
Dica: Você não precisa de espaços entre operadores e números. (+1 2) funciona da mesma forma que (+ 1 2).

2. Variáveis & Funções

Definindo variáveis

Use def para vincular um nome a um valor:

(def name "Alice")
(def age 30)
(def pi 3.14159)

(str "Hi, " name "! You are " age " years old.")
; → "Hi, Alice! You are 30 years old."

Definindo funções

Use defn para criar funções nomeadas:

;; Uma função que dobra um número
(defn double (x)
  (* x 2))

(double 21)  ; → 42

;; Uma função com múltiplos parâmetros
(defn greet (name greeting)
  (str greeting ", " name "!"))

(greet "Bob" "Good morning")
; → "Good morning, Bob!"

;; Lógica condicional com if
(defn abs-val (n)
  (if (< n 0) (- 0 n) n))

(abs-val -5)  ; → 5
(abs-val 3)   ; → 3

Funções anônimas

Use fn para funções avulsas sem nome:

(map (fn (x) (* x x)) '(1 2 3 4 5))
; → (1 4 9 16 25)

3. Listas & Funções de Ordem Superior

Listas são o pão com manteiga do Lisp. Elas armazenam sequências ordenadas de valores:

;; Criando listas
(def numbers (list 1 2 3 4 5))
(def fruits '("apple" "banana" "cherry"))

;; Acessando elementos
(first numbers)    ; → 1
(second numbers)   ; → 2
(last numbers)     ; → 5
(nth fruits 1)     ; → "banana"
(length numbers)   ; → 5

;; Construindo listas
(cons 0 numbers)        ; → (0 1 2 3 4 5)
(append numbers '(6 7)) ; → (1 2 3 4 5 6 7)
(range 1 11)            ; → (1 2 3 4 5 6 7 8 9 10)

Map, Filter, Reduce

As três operações de lista mais poderosas:

;; map — aplica uma função a cada elemento
(map double (range 1 6))
; → (2 4 6 8 10)

;; filter — mantém elementos que atendem a uma condição
(filter even? (range 1 11))
; → (2 4 6 8 10)

;; reduce — combina todos os elementos em um único valor
(reduce + 0 (range 1 101))
; → 5050  (soma de 1 a 100)

;; Componha-os: soma dos quadrados dos números pares de 1-10
(reduce + 0
  (map (fn (x) (* x x))
    (filter even? (range 1 11))))
; → 220  (4 + 16 + 36 + 64 + 100)

4. Dicionários

Dicionários armazenam pares chave-valor usando palavras-chave (prefixadas com :):

;; Criar um dicionário
(def person {:name "Alice" :age 30 :city "NYC"})

;; Acessar valores
(dict-get person :name)   ; → "Alice"
(dict-get person :age)    ; → 30

;; Atualizar (retorna um novo dict)
(def older (dict-set person :age 31))
(dict-get older :age)     ; → 31

;; Inspecionar
(dict-keys person)        ; → (:name :age :city)
(dict-values person)      ; → ("Alice" 30 "NYC")
(dict-has person :email)  ; → false

;; Mesclar dois dicts (o segundo prevalece em conflitos)
(dict-merge person {:email "alice@example.com" :age 31})
; → {:name "Alice" :age 31 :city "NYC" :email "alice@example.com"}

5. Projeto: Calculadora de Juros Compostos

Vamos construir uma calculadora financeira real que computa juros compostos anuais. Esta é a fórmula:

A = P × (1 + r/n)^(n×t)

Onde P = capital inicial, r = taxa anual, n = composições por ano, t = anos.

Definir a função principal

;; Compound interest formula using pow
;; A = P * (1 + r/n)^(n*t)
(defn compound-interest (principal rate compounds-per-year years)
  (let ((r (/ rate 100))
        (n compounds-per-year))
    (* principal (pow (+ 1 (/ r n)) (* n years)))))

;; Teste: $1000 a 5% ao ano, composição mensal, por 10 anos
(round (compound-interest 1000 5 12 10) 2)
; → 1647.01  (você ganhou $647.01 em juros!)

Adicionar detalhamento anual

;; Show balance at the end of each year
(defn yearly-breakdown (principal rate compounds-per-year years)
  (println (str "Investment: $" principal " at " rate "%"))
  (println (str "Compounding: " compounds-per-year "x/year for " years " years"))
  (println "---")
  (for-each year (range 1 (+ years 1))
    (let ((balance (compound-interest principal rate compounds-per-year year))
          (interest (- balance principal)))
      (println (str "Year " year ": $"
                   (round balance 2) "  (+"
                   (round interest 2) " interest)")))))

(yearly-breakdown 10000 7 12 5)
> (yearly-breakdown 10000 7 12 5) Investment: $10000 at 7% Compounding: 12x/year for 5 years --- Year 1: $10723 (+723 interest) Year 2: $11497 (+1497 interest) Year 3: $12327 (+2327 interest) Year 4: $13217 (+3217 interest) Year 5: $14171 (+4171 interest)

Comparar diferentes taxas

;; Compare multiple interest rates side by side
(defn compare-rates (principal years . rates)
  (println (str "Comparing $" principal " over " years " years:"))
  (for-each rate rates
    (let ((final (compound-interest principal rate 12 years))
          (gain (round (- final principal) 2)))
      (println (str "  " rate "% → $" (round final 2) "  (+" gain ")")))))

(compare-rates 10000 20 3 5 7 10)
> (compare-rates 10000 20 3 5 7 10) Comparing $10000 over 20 years: 3% → $18167 (+8167) 5% → $27126 (+17126) 7% → $40387 (+30387) 10% → $72890 (+62890)
Lição aprendida: Funções em EELisp podem receber parâmetros rest (. rates) para aceitar um número variável de argumentos. Isso é perfeito para comparar múltiplos valores.

6. Projeto: Gerenciador de Contatos com Tabelas

O EELisp possui um banco de dados SQLite integrado. Vamos usá-lo para gerenciar contatos — como um mini dBASE.

Criar a tabela

;; Define a contacts table with typed fields
(deftable contacts
  (name:string email:string phone:string city:string age:number))

;; The table is created. You can verify:
(describe contacts)
(tables)   ; → ("contacts")

Inserir registros

;; Insert contacts — each returns the new record with an auto-ID
(insert contacts {:name "Alice"   :email "alice@mail.com"   :phone "555-0101" :city "New York"    :age 30})
(insert contacts {:name "Bob"     :email "bob@mail.com"     :phone "555-0102" :city "San Francisco" :age 25})
(insert contacts {:name "Carol"   :email "carol@mail.com"   :phone "555-0103" :city "New York"    :age 35})
(insert contacts {:name "Dan"     :email "dan@mail.com"     :phone "555-0104" :city "Chicago"       :age 28})
(insert contacts {:name "Eve"     :email "eve@mail.com"     :phone "555-0105" :city "San Francisco" :age 32})

Consultar seus dados

;; See all contacts (displayed as a formatted table)
(query contacts)

;; Find contacts in New York
(query contacts :where "city = ?" :params (list "New York"))

;; Find contacts over 30, sorted by name
(query contacts :where "age > ?" :params (list 30) :order "name")

;; Count contacts
(count-records contacts)  ; → 5

;; Select only specific columns
(query contacts :select "name, city" :order "city")

Atualizar e excluir

;; Update Bob's city (record ID 2)
(update contacts 2 {:city "Los Angeles"})

;; Verify the change
(query contacts :where "name = ?" :params (list "Bob"))

;; Soft-delete Dan (he's marked deleted but not gone)
(delete contacts 4)
(count-records contacts)  ; → 4  (Dan is hidden)

;; Purge deleted records permanently
(pack contacts)
(count-records contacts)  ; → 4

Trabalhar com resultados de consulta no código

;; Extract records from a query and process them
(def nyc-people
  (records (query contacts :where "city = ?" :params (list "New York"))))

;; Get names of NYC contacts
(map (fn (r) (field-get r :name)) nyc-people)
; → ("Alice" "Carol")

;; Average age of all contacts
(let ((all (records (query contacts)))
      (ages (map (fn (r) (field-get r :age)) all)))
  (/ (reduce + 0 ages) (length ages)))
; → 30.5

7. Projeto: Controle de Despesas

Vamos construir um controle de despesas pessoal com resumos por categoria.

Configurar a tabela

(deftable expenses
  (description:string amount:number category:string date:string))

;; Add some expenses
(insert expenses {:description "Groceries"     :amount 85.50  :category "food"      :date "2026-02-01"})
(insert expenses {:description "Electric bill"  :amount 120.00 :category "utilities" :date "2026-02-03"})
(insert expenses {:description "Coffee shop"    :amount 12.75  :category "food"      :date "2026-02-05"})
(insert expenses {:description "Bus pass"       :amount 75.00  :category "transport" :date "2026-02-01"})
(insert expenses {:description "Restaurant"     :amount 45.00  :category "food"      :date "2026-02-10"})
(insert expenses {:description "Internet"       :amount 65.00  :category "utilities" :date "2026-02-03"})
(insert expenses {:description "Books"          :amount 32.99  :category "education" :date "2026-02-15"})

Consultar e analisar

;; Total spending
(let ((all (records (query expenses))))
  (reduce + 0 (map (fn (r) (field-get r :amount)) all)))
; → 436.24

;; Spending by category
(defn category-total (cat)
  (let ((rows (records (query expenses :where "category = ?" :params (list cat)))))
    (reduce + 0 (map (fn (r) (field-get r :amount)) rows))))

(println (str "Food:      $" (category-total "food")))
(println (str "Utilities: $" (category-total "utilities")))
(println (str "Transport: $" (category-total "transport")))
(println (str "Education: $" (category-total "education")))
> Food: $143.25 Utilities: $185 Transport: $75 Education: $32.99

Encontrar as maiores despesas

;; Top 3 expenses by amount
(query expenses :order "amount" :desc true :limit 3)

8. Projeto: Construindo uma Lista de Tarefas

Um mini-projeto clássico que mostra como combinar tabelas com funções auxiliares.

Criar a tabela e funções auxiliares

(deftable todos
  (title:string priority:number done:bool))

;; Helper: add a todo
(defn add-todo (title priority)
  (insert todos {:title title :priority priority :done false})
  (println (str "Added: " title)))

;; Helper: mark done
(defn done! (id)
  (update todos id {:done true})
  (println "Done!"))

;; Helper: show pending todos
(defn pending ()
  (query todos :where "done = ?" :params (list false) :order "priority"))

;; Helper: show completed todos
(defn completed ()
  (query todos :where "done = ?" :params (list true)))

Usar

;; Add tasks (lower priority number = more important)
(add-todo "Write README"       1)
(add-todo "Fix login bug"     1)
(add-todo "Update tests"      2)
(add-todo "Refactor CSS"      3)
(add-todo "Deploy to staging" 2)

;; View pending tasks
(pending)

;; Complete a task
(done! 1)  ; mark "Write README" as done

;; See what's left
(pending)

;; See what's completed
(completed)
Dica: Você pode salvar essas definições de função em um arquivo .el e carregá-las com :load todo-helpers.el toda vez que abrir o REPL.

9. Projeto: Livro de Receitas com Navegação & Edição

O EELisp possui dois comandos de visualização interativa: browse mostra uma tabela/grade navegável, e edit abre um formulário CRUD. Vamos construir um livro de receitas usando ambos.

Criar a tabela

Nossa tabela de receitas usa vários tipos de campo: string para texto curto, number para quantidades, bool para indicadores, e memo para notas mais longas.

(deftable recipes
  (name:string
   cuisine:string
   servings:number
   vegetarian:bool
   notes:memo))

Adicionar algumas receitas

(insert recipes {:name "Pasta Carbonara"
                 :cuisine "Italian"
                 :servings 4
                 :vegetarian false
                 :notes "Use guanciale, not bacon."})

(insert recipes {:name "Vegetable Curry"
                 :cuisine "Indian"
                 :servings 6
                 :vegetarian true
                 :notes "Toast spices first."})

(insert recipes {:name "Miso Soup"
                 :cuisine "Japanese"
                 :servings 2
                 :vegetarian true
                 :notes "Don't boil the miso."})

Navegar pelas receitas (visualização em tabela)

O comando browse abre uma tabela/grade navegável mostrando todos os registros de uma vez:

;; Open the table view
(browse recipes)

A visualização em tabela mostra todos os registros em uma grade. Clique em uma linha para selecioná-la, ou use Anterior/Próximo para navegar.

Dica: Você também pode executar browse, edit e defform a partir de um bloco ```eelisp nas suas notas Markdown. Pressione ⌘⇧Return e o formulário ou tabela abre como um painel lateral interativo ao lado do editor — assim você pode ver seus dados e suas notas ao mesmo tempo.

Editar com um formulário CRUD

O comando edit abre um formulário estilo dBASE para operações CRUD completas:

;; Open the CRUD form
(edit recipes)

O formulário mostra um registro por vez. Você pode:

Filtragem e ordenação

Tanto browse quanto edit suportam :where, :params e :order:

;; Browse only vegetarian recipes
(browse recipes :where "vegetarian = ?" :params (list true))

;; Edit sorted by cuisine
(edit recipes :order "cuisine")

;; Compare with text table output
(query recipes :select (list "name" "cuisine" "servings"))

Armazenamento persistente

Por padrão, os dados ficam na memória e são perdidos quando você fecha o aplicativo. Para persistir suas receitas:

;; Create a database file in your folder
:db new recipes

;; Now all deftable/insert/update calls are persisted
;; Check current database
:db

;; Switch back to in-memory
:db memory
Dica: Salve isso como recipes.lisp e carregue com :load recipes.lisp. Se sua pasta tiver um arquivo eelisp.db, o REPL conecta-se a ele automaticamente quando você abre a pasta.

10. Projeto: Formulários Calculadora com Campos Computados

O comando defform cria formulários interativos independentes com campos computados. Campos computados atualizam automaticamente quando você altera valores de entrada — eles não são salvos em nenhum banco de dados.

Calculadora de juros compostos

Vamos construir uma calculadora que computa juros compostos a partir de três entradas:

(defform compound-interest
  (principal:number
   rate:number
   years:number)
  :computed
  ((total-amount (* principal (pow (+ 1 (/ rate 100)) years)))
   (interest (- total-amount principal))))

Isso cria um formulário com três campos editáveis (principal, rate, years) e dois campos computados (total-amount, interest). Experimente alterar os valores — os campos computados atualizam instantaneamente.

Como defform funciona

A sintaxe é:

(defform form-name
  (field1:type field2:type ...)
  :computed
  ((computed1 expression1)
   (computed2 expression2)))

Mais exemplos

Conversor de temperatura:

(defform temperature
  (celsius:number)
  :computed
  ((fahrenheit (+ (* celsius 1.8) 32))
   (kelvin (+ celsius 273.15))))

Calculadora de IMC:

(defform bmi
  (weight-kg:number
   height-cm:number)
  :computed
  ((height-m (/ height-cm 100))
   (bmi (/ weight-kg (* height-m height-m)))))
Dica: Carregue o arquivo de exemplo com :load compound-calculator.lisp para ver isso em ação.

11. Projeto: Agenda Pessoal — Itens & Categorias

O EELisp inclui um gerenciador de informações pessoais inspirado no Lotus Agenda construído sobre a camada de banco de dados. Pense nele como uma lista de tarefas programável combinada com um organizador de conhecimento — você digita texto livre e a estrutura emerge das categorias.

Adicionar seus primeiros itens

Itens são focados em texto. Apenas digite o que está na sua mente:

;; Simple item
(add-item "Finish quarterly report for Sarah")

;; With due date and priority
(add-item "Call dentist to reschedule"
  :when "2026-03-01" :priority 2)

;; With a category and notes
(add-item "Buy groceries"
  :category "personal"
  :notes "milk, eggs, bread, coffee")

;; High-priority work item
(add-item "Deploy v2.1 to staging"
  :when "2026-02-26" :priority 1)

;; A meeting
(add-item "Team standup" :when "2026-02-24" :priority 3)
> (add-item "Finish quarterly report for Sarah") Item #1: Finish quarterly report for Sarah > (add-item "Call dentist to reschedule" :when "2026-03-01" :priority 2) Item #2: Call dentist to reschedule When: 2026-03-01 Priority: 2

Navegue pelos seus itens

O comando items mostra todos os itens em uma visualização de tabela:

;; Ver todos os itens
(items)
> (items) id | text | when | priority | categories ---+-----------------------------------+------------+----------+----------- 1 | Finish quarterly report for Sarah | | | 2 | Call dentist to reschedule | 2026-03-01 | 2 | 3 | Buy groceries | | | personal 4 | Deploy v2.1 to staging | 2026-02-26 | 1 | 5 | Team standup | 2026-02-24 | 3 | 5 record(s)

Defina categorias

As categorias são hierárquicas — use separadores /. Você também pode definir grupos exclusivos onde um item só pode pertencer a um filho:

;; Categorias de trabalho
(defcategory work)
(defcategory work/projects)
(defcategory work/meetings)
(defcategory work/admin)

;; Categorias pessoais
(defcategory personal)
(defcategory personal/health)
(defcategory personal/errands)

;; Grupo exclusivo: o item só pode ser high, medium OU low
(defcategory priority :exclusive true
  :children (high medium low))

;; Visualizar a árvore
(categories)
> (categories) personal personal/errands personal/health priority [exclusive] priority/high priority/low priority/medium work work/admin work/meetings work/projects

Atribua categorias aos itens

Use assign para etiquetar itens. Categorias exclusivas removem automaticamente as irmãs:

;; Etiquetar itens com categorias
(assign 1 "work/projects")
(assign 2 "personal/health")
(assign 4 "work/projects")
(assign 5 "work/meetings")

;; Definir prioridades
(assign 1 "priority/high")
(assign 4 "priority/high")
(assign 2 "priority/medium")

;; Verificar um item
(item-get 1)
> (item-get 1) Item #1: Finish quarterly report for Sarah Categories: priority/high, work/projects

Filtrar e pesquisar

;; Encontrar todos os itens de trabalho
(items :category "work")

;; Pesquisar por texto
(items :search "quarterly")

;; Apenas itens de prioridade 1
(items :priority 1)

;; Itens com vencimento antes de março
(items :when-before "2026-03-01")

;; Quantos itens eu tenho?
(item-count)

Atualizar e concluir itens

;; Atualizar propriedades
(item-set 1 :priority 1 :when "2026-02-28")

;; Mudar prioridade — exclusiva! Remove "high" automaticamente
(assign 1 "priority/low")
(item-get 1)  ; agora mostra priority/low, não priority/high

;; Marcar item como concluído (exclusão lógica)
(item-done 3)  ; "Buy groceries" — pronto!
(item-count)     ; → 4 (era 5)

;; Remover uma categoria
(unassign 2 "personal/health")
Como funciona: Itens e categorias são armazenados em tabelas SQLite (_items, _categories, _rules), criadas automaticamente. Isso significa que sua agenda persiste se você usar um arquivo de banco de dados (:db myagenda.db). As categorias são arrays JSON dentro de cada registro de item, e as propriedades (when, priority, etc.) são armazenadas como um objeto JSON.
Dica: Combine itens de agenda com a camada de banco de dados. Você poderia escrever um helper que consulta seus itens e gera um arquivo de resumo diário em Markdown.

12. Regras de Auto-Categorização — A Magia do Lotus Agenda

Defina regras que categorizam automaticamente itens com base em seu conteúdo. Isso é o que tornou o Lotus Agenda revolucionário em 1988 — e agora está no EELisp.

Defina regras de correspondência de texto

;; Regra: itens contendo "URGENT" recebem prioridade alta
(defrule urgent-flag
  :when (str-contains text "URGENT")
  :assign "priority/high")

;; Regra: reuniões e ligações → work/meetings
(defrule meeting-detect
  :when (or (str-contains text "meeting")
            (str-contains text "call with")
            (str-contains text "standup"))
  :assign "work/meetings")

;; Regra: itens de compras → personal/errands
(defrule errand-detect
  :when (or (str-contains text "buy ")
            (str-contains text "groceries"))
  :assign "personal/errands")
Rules defined: urgent-flag, meeting-detect, errand-detect

Aplique regras aos itens existentes

;; Aplicar regras a todos os itens (retorna contagem de alterações)
(apply-rules)
;; → 3 (itens que corresponderam às regras)

;; Aplicar a um único item
(apply-rules 2)

;; Verificar os resultados
(item-get 2)
;; → agora mostra Categories: priority/high (se continha "URGENT")

Ative a auto-categorização

;; Ativar auto-aplicação — regras executam em cada add-item
(auto-categorize true)

;; Agora novos itens são categorizados automaticamente!
(add-item "URGENT: Deploy hotfix to production")
;; → automaticamente atribuído priority/high

(add-item "Buy milk and eggs this weekend")
;; → automaticamente atribuído personal/errands

(add-item "Standup with the team at 9am")
;; → automaticamente atribuído work/meetings

Use regex para extração de datas

;; Extrair datas ISO do texto do item usando :action
(defrule date-extract
  :when (str-matches text "\\b(\\d{4}-\\d{2}-\\d{2})\\b")
  :action (item-set id :when (match 1)))

;; Itens com datas recebem :when automaticamente
(add-item "Deadline is 2026-04-01")
(item-get 8)
;; → When: 2026-04-01
str-matches retorna grupos de captura, e (match 1) extrai o primeiro grupo.

Gerencie suas regras

;; Listar todas as regras
(rules)
;; → urgent-flag: (str-contains text "URGENT")
;;   meeting-detect: (or (str-contains text "meeting") ...)
;;   errand-detect: (or (str-contains text "buy ") ...)
;;   date-extract: (str-matches text "\\b(\\d{4}-\\d{2}-\\d{2})\\b")

;; Excluir uma regra que você não precisa mais
(drop-rule "errand-detect")

;; Desativar auto-categorização
(auto-categorize false)
Como funciona: As condições das regras são armazenadas como expressões Lisp serializadas na tabela _rules. Quando avaliadas, os campos de cada item (text, notes, id, categories, e todas as propriedades) são vinculados como símbolos no ambiente de avaliação da regra. O atalho :assign gera ações (assign id "category"), enquanto :action permite executar qualquer expressão Lisp.
Dica: As regras são expressões Lisp completas — combine and/or/not, use has-category para verificar atribuições existentes, (get :property) para testes de propriedades, e str-matches para padrões regex. Você pode construir lógica de categorização arbitrariamente complexa.

13. Visualizações — Perspectivas Dinâmicas

Visualizações são consultas salvas que filtram, ordenam e agrupam seus itens. Defina uma visualização uma vez e use show a qualquer momento para ver os itens correspondentes mais recentes — como um painel ao vivo para sua agenda.

Defina suas primeiras visualizações

Visualizações usam expressões de filtro — o mesmo contexto das regras (text, categories, propriedades, has-category, overdue?):

;; Um quadro de todos os itens de trabalho, agrupados por categoria
(defview work-board
  :source items
  :filter (has-category "work")
  :group-by category
  :sort-by when)

;; Apenas itens de alta prioridade
(defview urgent
  :source items
  :filter (= priority "1")
  :sort-by when)

;; Itens sem categorias (caixa de entrada não categorizada)
(defview inbox
  :source items
  :filter (= (length categories) 0))

;; Itens atrasados
(defview overdue
  :source items
  :filter (overdue?)
  :sort-by when)

Exiba uma visualização

Use show para exibir os resultados de uma visualização. Visualizações agrupadas são renderizadas como uma árvore com cabeçalhos de categoria:

;; Exibir o quadro de trabalho
(show work-board)
> (show work-board) ▸ work/meetings (2) Team standup Call with client ▸ work/projects (3) Deploy v2.1 to staging Finish quarterly report Code review PR #42
;; Exibir itens urgentes
(show urgent)
> (show urgent) id | text | when | priority ---+-----------------------------------+------------+--------- 4 | Deploy v2.1 to staging | 2026-02-26 | 1 6 | URGENT: Deploy hotfix | | 1 2 record(s)

Use helpers de data para visualizações baseadas em tempo

O EELisp fornece helpers de data que são perfeitos para filtros de visualização:

;; Data de hoje como string ISO
(today)
; → "2026-02-21"

;; Adicionar dias, semanas ou meses
(date-add (today) 7 :days)
; → "2026-02-28"

(date-add (today) 1 :months)
; → "2026-03-21"

;; Diferença em dias
(date-diff "2026-02-21" "2026-03-01")
; → 8

Gerencie suas visualizações

;; Listar todas as visualizações definidas
(views)
;; → work-board: filter=(has-category "work") group-by=category sort-by=when
;;   urgent:     filter=(= priority "1") sort-by=when
;;   inbox:      filter=(= (length categories) 0)
;;   overdue:    filter=(overdue?) sort-by=when

;; Excluir uma visualização
(drop-view "inbox")
Como funciona: As visualizações são armazenadas na tabela _views com suas expressões de filtro serializadas como Lisp. Quando você usa show em uma visualização, ela busca todos os itens, avalia o filtro em cada um (vinculando todos os campos do item como símbolos), ordena os resultados e opcionalmente os agrupa. Itens com propriedades ausentes são silenciosamente excluídos — sem erros.
Dica: Use o helper overdue? em filtros para encontrar itens que passaram da data :when. Combine com and/or para visualizações complexas: :filter (and (has-category "work") (overdue?)) mostra itens de trabalho atrasados.

14. Integração com Calendário

Conecte sua agenda com datas do calendário. Visualize o que está agendado para um dia específico, navegue por um intervalo de datas e adicione rapidamente itens para hoje — tudo a partir do REPL.

Visualize itens por data

Use items-on para ver tudo agendado para uma data específica:

;; O que está acontecendo na segunda-feira?
(items-on "2026-02-24")

;; Use (today) para datas dinâmicas
(items-on (today))
> (items-on "2026-02-24") id | text | when | priority | categories ---+-----------------------------------+------------+----------+----------- 5 | Team standup | 2026-02-24 | 3 | work/meetings 1 record(s)

Navegue por um intervalo de datas

Use items-between para ver itens em um intervalo de datas (inclusivo em ambas as extremidades):

;; O que tem esta semana?
(items-between "2026-02-24" "2026-02-28")

;; Próximos 7 dias usando helpers de data
(items-between (today) (date-add (today) 7 :days))

;; O mês inteiro
(items-between "2026-02-01" "2026-02-28")
> (items-between "2026-02-24" "2026-02-28") id | text | when | priority | categories ---+-----------------------------------+------------+----------+----------- 5 | Team standup | 2026-02-24 | 3 | work/meetings 4 | Deploy v2.1 to staging | 2026-02-26 | 1 | work/projects 1 | Finish quarterly report | 2026-02-28 | | work/projects 3 record(s)

Adicione rapidamente itens para hoje

add-item-today cria um item com :when automaticamente definido para a data de hoje:

;; Adição rápida para hoje
(add-item-today "Review pull requests" :priority 1)
(add-item-today "Quick grocery run" :notes "milk, bread")
(add-item-today "Team sync" :category "work/meetings")
> (add-item-today "Review pull requests" :priority 1) Item #9: Review pull requests When: 2026-02-21 Priority: 1

Combine com helpers de data e visualizações

Helpers de data e comandos de calendário funcionam muito bem junto com visualizações:

;; Data de hoje como string ISO
(today)               ; → "2026-02-21"

;; Aritmética de datas
(date-add (today) 7 :days)     ; → "2026-02-28"
(date-add (today) 1 :months)   ; → "2026-03-21"
(date-diff "2026-02-21" "2026-03-01")  ; → 8 (dias)

;; Definir uma visualização para os itens desta semana
(defview this-week
  :source items
  :filter (and (>= when (today))
              (<= when (date-add (today) 7 :days)))
  :sort-by when)
Dica: add-item-today é um atalho para (add-item text :when (today)). Ele suporta as mesmas opções :priority, :category e :notes que add-item. Combine com regras de auto-categorização para organização sem esforço.
Referência de comandos de calendário:

15. Entrada Inteligente & HTTP

O comando add usa análise de linguagem natural baseada em regex para extrair automaticamente datas, prioridades e pessoas do seu texto. Combinado com builtins de HTTP e JSON, o EELisp se torna uma poderosa ferramenta de automação.

Criação de itens com linguagem natural

Basta digitar o que está em mente — add descobre os metadados:

;; Datas são extraídas automaticamente
(add "Meet Alice tomorrow for coffee")
;; → :when = data de amanhã

(add "Call Bob next Monday about the project")
;; → :when = data da próxima segunda, :who = Bob

(add "Review docs end of month")
;; → :when = último dia do mês atual

(add "Deploy hotfix in 3 days")
;; → :when = 3 dias a partir de agora
> (add "Meet Alice tomorrow for coffee") Item #1: Meet Alice for coffee When: 2026-02-23 Who: Alice

Detecção de prioridade

Expresse urgência com palavras ou pontos de exclamação:

;; Prioridade baseada em palavras
(add "URGENT fix server crash")
;; → :priority 1

;; Pontos de exclamação: !!! = 1, !! = 2, ! = 3
(add "Review PR !!")
;; → :priority 2

(add "Update README low priority")
;; → :priority 4

Pessoas e @menções

Pessoas são detectadas a partir de @menções, preposições e verbos de ação:

;; @menções
(add "Review code for @carlos")
;; → :who = carlos

;; Padrões Verbo + Nome
(add "Email Sarah March 15 about renewal")
;; → :who = Sarah, :when = March 15

;; Padrões Preposição + Nome
(add "Meeting with John this weekend")
;; → :who = John, :when = Saturday

Pré-visualize com smart-parse

Use smart-parse para ver o que seria extraído sem criar um item:

(smart-parse "Meet Alice tomorrow for coffee !!")
> (smart-parse "Meet Alice tomorrow for coffee !!") {:text "Meet Alice for coffee" :when "2026-02-23" :priority 2 :who ("Alice")}

Builtins de HTTP e JSON

O EELisp inclui builtins genéricos de HTTP e JSON para integração com APIs:

;; Analisar JSON para dados EELisp
(json-parse "{\"name\": \"Alice\", \"scores\": [95, 87]}")
;; → {:name "Alice" :scores (95 87)}

;; Converter EELisp para JSON
(json-stringify {:name "Bob" :active true})
;; → "{\"active\":true,\"name\":\"Bob\"}"

;; Requisições HTTP
(http-get "https://api.example.com/data")
;; → {:status 200 :body "..."}

(http-post "https://api.example.com/items"
  (json-stringify {:title "New item"})
  :content-type "application/json")
;; → {:status 201 :body "..."}
Padrões de data reconhecidos: Datas ISO (2026-03-15), tomorrow/today/yesterday, next Monday, this weekend, in 3 days, in 2 weeks, end of week, end of month, March 15.
Dica: add funciona perfeitamente com regras de auto-categorização. Após o smart-parsing extrair metadados, as regras ainda são executadas — então (add "URGENT meeting with Bob tomorrow") pode simultaneamente definir a data, prioridade e acionar sua regra "meeting-detect" para atribuir work/meetings.

16. Itens Recorrentes & Modelos

Itens que se repetem em um cronograma e blueprints reutilizáveis para tarefas comuns.

Passo 1 — Crie um item recorrente

Adicione :recur a qualquer chamada add-item. Padrões embutidos: :daily, :weekly, :monthly.

(add-item "Team standup" :when "2026-02-24" :recur :weekly :category "work")
;; Item #1: Team standup
;;   When: 2026-02-24
;;   Recurrence: weekly
;;   Categories: work

Passo 2 — Intervalos personalizados

Use (every N :unit) para padrões de recorrência personalizados:

(add-item "Quarterly review" :when "2026-04-01" :recur (every 3 :months))
(add-item "Biweekly report" :when "2026-02-28" :recur (every 2 :weeks))

Passo 3 — Conclua um item recorrente

Quando você marca um item recorrente como concluído, o sistema cria automaticamente a próxima ocorrência:

(item-done 1)
;; Item #4: Team standup
;;   When: 2026-03-03          ← avançou 7 dias
;;   Recurrence: weekly
;;   Categories: work
Dica: O novo item preserva todas as propriedades — texto, categorias, prioridade, padrão de recorrência e notas. Apenas a data avança.

Passo 4 — Defina um modelo

Modelos são blueprints reutilizáveis para itens que você cria com frequência:

(deftemplate weekly-review
  :text "Weekly review: reflect on goals"
  :category "work/admin"
  :priority 2
  :notes "1. What went well?\n2. What to improve?")
;; "Template 'weekly-review' defined"

Passo 5 — Crie itens a partir de modelos

Use from-template com substituições opcionais:

(from-template weekly-review :when "2026-03-07")
;; Item #5: Weekly review: reflect on goals
;;   When: 2026-03-07
;;   Priority: 2
;;   Categories: work/admin

;; Substituir qualquer campo
(from-template weekly-review :when "2026-03-14" :priority 1)

Passo 6 — Gerencie modelos

Liste e remova modelos:

(templates)
;; weekly-review — "Weekly review: reflect on goals" [work/admin] !2

(drop-template weekly-review)
;; "Template 'weekly-review' removed"
Dica: Modelos também podem incluir :recur — ex. (deftemplate standup :text "Daily standup" :recur :daily). Todo item criado a partir dele será automaticamente recorrente.

17. Múltiplas Agendas

Gerencie agendas separadas para trabalho, pessoal e projetos. Cada agenda é um banco de dados SQLite independente com seus próprios itens, categorias, regras, visualizações e modelos.

Passo 1: Abra uma segunda agenda

Use open-agenda para criar um novo arquivo de banco de dados e alternar para ele:

;; Sua agenda padrão já está ativa
(add-item "Personal task" :category "personal")

;; Abrir uma agenda de trabalho — cria o arquivo se não existir
(open-agenda "work.db")
;; "Opened agenda 'work' at /path/to/work.db"

(add-item "Deploy to staging" :when "2026-03-01" :priority 1)

Passo 2: Alterne entre agendas

Use use-agenda para alternar. Os itens são completamente isolados entre agendas:

;; Voltar para a padrão
(use-agenda memory)
(items)  ;; → apenas "Personal task"

;; Alternar para trabalho
(use-agenda work)
(items)  ;; → apenas "Deploy to staging"

Passo 3: Liste e gerencie agendas

;; Ver todas as agendas abertas
(agendas)
;; Agendas:
;;   memory — :memory:
;;   work [active] — /path/to/work.db

;; Fechar uma agenda que você não precisa mais
(close-agenda work)

Passo 4: Exporte e importe

Faça backup da sua agenda para JSON, ou transfira itens entre agendas:

;; Exportar agenda atual
(export-agenda "memory" :format :json :path "backup.json")
;; "Exported agenda 'memory' to /path/to/backup.json"

;; Importar em uma agenda diferente
(open-agenda "archive.db")
(import-agenda "backup.json")
;; "Imported: 15 items, 4 categories, 3 rules, 2 views, 1 templates"
Dica: Os nomes das agendas são derivados dos nomes dos arquivos — work.db se torna work, :memory: se torna memory. Use nomes de arquivo descritivos para facilitar a alternância.

18. Barra Lateral da Agenda & Integração com Calendário

A barra lateral do EEditor tem três modos: Arquivos, Calendário e Agenda. Os dois últimos se integram diretamente com seu banco de dados de agenda EELisp, oferecendo uma visão geral visual sem precisar escrever consultas.

A Barra Lateral da Agenda

Clique no ícone de prancheta no controle segmentado da barra lateral para alternar para o modo Agenda. Ele consulta automaticamente seu banco de dados e organiza os itens em três seções:

Cada item mostra um ponto de prioridade (vermelho / laranja / amarelo / cinza), o texto do item, categorias e uma data formatada. Pressione o botão de atualizar para recarregar após adicionar itens no REPL.

Como funciona por trás dos bastidores

A barra lateral da agenda não duplica a lógica do banco de dados. Em vez disso, o AgendaViewModel chama interpreter.eval() com os mesmos builtins EELisp que você usaria no REPL:

;; O que a barra lateral da Agenda executa internamente:
(items :when-before "2026-02-23")    ;; → Seção Atrasados
(items-on "2026-02-23")               ;; → Seção Hoje
(items-between "2026-02-24" "2026-03-02") ;; → Seção Próximos

Calendário + Agenda

A barra lateral do Calendário também se integra com sua agenda. Cada célula de dia mostra indicadores de ponto duplo:

Quando você clica em um dia, o painel de detalhes mostra as seções Arquivos e Agenda, para que você possa ver sua atividade de escrita junto com suas tarefas agendadas.

Experimente você mesmo

;; 1. Adicione alguns itens com datas
(add-item "Review PR" :when (today) :priority 1)
(add-item "Team meeting" :when (date-add (today) 1 :days) :category "work/meetings")
(add-item "Deploy v2" :when (date-add (today) 3 :days) :priority 2)
(add-item "Overdue task" :when "2026-01-15")

;; 2. Alterne para a aba Agenda na barra lateral
;;    → Você verá:  Atrasados (1) | Hoje (1) | Próximos 7 Dias (2)

;; 3. Alterne para a aba Calendário na barra lateral
;;    → Hoje tem um ponto laranja; clique nele para ver o item
Dica: A barra lateral da GUI e o REPL compartilham o mesmo interpretador e banco de dados. Qualquer item que você adicionar no REPL aparece na barra lateral após uma atualização, e vice-versa. Este é o poder da integração EELisp-GUI — um motor de dados, múltiplas visualizações.

19. Dicas & Padrões

Pipe para legibilidade

Use a macro pipe para encadear operações da esquerda para a direita em vez de aninhar:

;; Em vez desta expressão profundamente aninhada:
(str-join (map str-upper (filter (fn (s) (str-starts-with s "a")) (str-split "apple,avocado,banana,apricot" ","))) ", ")

;; Escreva isto:
(pipe "apple,avocado,banana,apricot"
  (str-split ",")
  (filter (fn (s) (str-starts-with s "a")))
  (map str-upper)
  (str-join ", "))
; → "APPLE, AVOCADO, APRICOT"

Salvar e carregar scripts

Escreva suas funções em um arquivo e carregue-as no REPL:

;; No REPL, carregue um arquivo de script:
:load ~/scripts/finance.el

;; Ou clique com o botão direito em um arquivo .el na barra lateral e
;; selecione "Run in REPL"

Folha de referência de comandos do REPL

ComandoDescrição
:helpMostrar todos os comandos do REPL
:load <file>Carregar e executar um script
:dbMostrar caminho do banco de dados atual
:db <path>Alternar para um arquivo de banco de dados
:db new <name>Criar um novo arquivo de banco de dados
:db memoryAlternar para banco de dados em memória
:envListar todos os símbolos definidos
:clearLimpar a saída
:resetReiniciar o interpretador
(tables)Listar todas as tabelas no banco de dados atual
(describe tablename)Mostrar o esquema de uma tabela
(drop-table tablename)Excluir uma tabela

Padrões comuns

PadrãoExemplo
Transformar uma lista(map inc numbers)
Filtrar uma lista(filter even? numbers)
Agregar uma lista(reduce + 0 numbers)
Consultar + processar(map (fn (r) (field-get r :name)) (records (query ...)))
Condicional(if (> x 0) "positive" "non-positive")
Escopo local(let ((x 1) (y 2)) (+ x y))
Loop(loop ((i 0)) (= i 10) (recur (inc i)))
Matemática(pow 2 10), (round 3.14159 2), (abs -5)
Visualização em tabela(browse tasks)
Formulário CRUD(edit tasks)
Formulário calculadora(defform name (fields) :computed ((name expr)))
Adicionar item de agenda(add-item "text" :when "date" :priority n)
Navegar itens(items :category "work" :search "text")
Definir categoria(defcategory work/projects)
Atribuir categoria(assign 1 "work/projects")
Concluir item(item-done 1)
Definir regra(defrule name :when (str-contains text "URGENT") :assign "priority/high")
Auto-categorizar(auto-categorize true)
Aplicar regras(apply-rules)
Regra com regex(defrule name :when (str-matches text "pattern") :action expr)
Definir visualização(defview name :source items :filter (has-category "work") :group-by category)
Exibir visualização(show work-board)
Data de hoje(today), (date-add (today) 7 :days)
Itens para uma data(items-on "2026-02-24")
Itens em intervalo(items-between (today) (date-add (today) 7 :days))
Adição rápida hoje(add-item-today "text" :priority 1)
Adição inteligente (NLP)(add "Meet Alice tomorrow !!")
Pré-visualizar análise(smart-parse "Call Bob next Monday")
Analisar JSON(json-parse "{\"a\": 1}")
Serializar JSON(json-stringify {:name "Alice"})
HTTP GET(http-get "https://api.example.com/data")
Item recorrente(add-item "Task" :when "2026-03-01" :recur :weekly)
Intervalo personalizado(add-item "Review" :when "2026-04-01" :recur (every 3 :months))
Definir modelo(deftemplate standup :text "Standup" :recur :daily)
Usar modelo(from-template standup :when "2026-03-01")
Abrir agenda(open-agenda "work.db")
Alternar agenda(use-agenda work)
Listar agendas(agendas)
Exportar agenda(export-agenda "work" :format :json :path "backup.json")
Importar agenda(import-agenda "backup.json")
O que vem a seguir? Tente combinar tabelas e funções para construir suas próprias ferramentas: um registro de leitura, um livro de receitas, um rastreador de treinos ou um CRM pessoal. O REPL é seu playground.