about summary refs log tree commit diff stats
path: root/064list.mu
blob: d669ec2c9568cc2da4cee8c65be35a95352b38d1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
4
#+TITLE: Cetus

Cetus is a wallpaper manager written in Go. It uses [[https://source.unsplash.com/][Unsplash Source]] to get
wallpapers.

*Note*: Cetus is a work in-progress & many features are yet to be implemented.

* Features
- set a random/daily/weekly wallpaper
- set a specific photo as wallpaper
- set a wallpaper from a specific user, user's likes, a collection or a search
  term
- customize wallpaper size
* Demo
I just run some cetus commands on my computer, nothing fancy. I'll make better
demo videos someday.

| Version | Video                                                                |
|---------+----------------------------------------------------------------------|
| v0.2.0  | https://diode.zone/videos/watch/12db31e1-3517-4888-ad06-55f3859447a1 |
* Dependency
- [[https://feh.finalrewind.org/][feh]]
* Installation
** From source
*** Download the source
- Using Go
#+BEGIN_SRC sh
go get https://framagit.org/andinus/cetus.git
#+END_SRC

- Using git
#+BEGIN_SRC sh
git clone https://framagit.org/andinus/cetus.git
#+END_SRC

- Using curl
#+BEGIN_SRC sh
# get the master branch (supported formats: zip, tar.gz, tar.bz2, tar)
curl -o cetus-master.tar.gz \
     https://framagit.org/andinus/cetus/-/archive/master/cetus-master.tar.gz
#+END_SRC
*** Compile cetus
Navigate to the source directory & compile cetus.

#+BEGIN_SRC sh
# compile
go build

# install in $PATH
go install
#+END_SRC
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
# A list links up multiple objects together to make them easier to manage.
#
# The objects must be of the same type. If you want to store multiple types in
# a single list, use an exclusive-container.

container list:_elem [
  value:_elem
  next:&:list:_elem
]

def push x:_elem, l:&:list:_elem -> result:&:list:_elem/contained-in:l [
  local-scope
  load-inputs
  result <- new {(list _elem): type}
  *result <- merge x, l
]

def first in:&:list:_elem -> result:_elem [
  local-scope
  load-inputs
  result <- get *in, value:offset
]

def rest in:&:list:_elem -> result:&:list:_elem/contained-in:in [
  local-scope
  load-inputs
  result <- get *in, next:offset
]

scenario list-handling [
  run [
    local-scope
    x:&:list:num <- push 3, null
    x <- push 4, x
    x <- push 5, x
    10:num/raw <- first x
    x <- rest x
    11:num/raw <- first x
    x <- rest x
    12:num/raw <- first x
    20:&:list:num/raw <- rest x
  ]
  memory-should-contain [
    10 <- 5
    11 <- 4
    12 <- 3
    20 <- 0  # nothing left
  ]
]

def length l:&:list:_elem -> result:num [
  local-scope
  load-inputs
  result <- copy 0
  {
    break-unless l
    result <- add result, 1
    l <- rest l
    loop
  }
]

# insert 'x' after 'in'
def insert x:_elem, in:&:list:_elem -> in:&:list:_elem [
  local-scope
  load-inputs
  new-node:&:list:_elem <- new {(list _elem): type}
  *new-node <- put *new-node, value:offset, x
  next-node:&:list:_elem <- get *in, next:offset
  *in <- put *in, next:offset, new-node
  *new-node <- put *new-node, next:offset, next-node
]

scenario inserting-into-list [
  local-scope
  list:&:list:num <- push 3, null
  list <- push 4, list
  list <- push 5, list
  run [
    list2:&:list:num <- rest list  # inside list
    list2 <- insert 6, list2
    # check structure
    list2 <- copy list
    10:num/raw <- first list2
    list2 <- rest list2
    11:num/raw <- first list2
    list2 <- rest list2
    12:num/raw <- first list2
    list2 <- rest list2
    13:num/raw <- first list2
  ]
  memory-should-contain [
    10 <- 5  # scanning next
    11 <- 4
    12 <- 6  # inserted element
    13 <- 3
  ]
]

scenario inserting-at-end-of-list [
  local-scope
  list:&:list:num <- push 3, null
  list <- push 4, list
  list <- push 5, list
  run [
    list2:&:list:num <- rest list  # inside list
    list2 <- rest list2  # now at end of list
    list2 <- insert 6, list2
    # check structure like before
    list2 <- copy list
    10:num/raw <- first list2
    list2 <- rest list2
    11:num/raw <- first list2
    list2 <- rest list2
    12:num/raw <- first list2
    list2 <- rest list2
    13:num/raw <- first list2
  ]
  memory-should-contain [
    10 <- 5  # scanning next
    11 <- 4
    12 <- 3
    13 <- 6  # inserted element
  ]
]

scenario inserting-after-start-of-list [
  local-scope
  list:&:list:num <- push 3, null
  list <- push 4, list
  list <- push 5, list
  run [
    list <- insert 6, list
    # check structure like before
    list2:&:list:num <- copy list
    10:num/raw <- first list2
    list2 <- rest list2
    11:num/raw <- first list2
    list2 <- rest list2
    12:num/raw <- first list2
    list2 <- rest list2
    13:num/raw <- first list2
  ]
  memory-should-contain [
    10 <- 5  # scanning next
    11 <- 6  # inserted element
    12 <- 4
    13 <- 3
  ]
]

# remove 'x' from its surrounding list 'in'
#
# Returns null if and only if list is empty. Beware: in that case any other
# pointers to the head are now invalid.
def remove x:&:list:_elem/contained-in:in, in:&:list:_elem -> in:&:list:_elem [
  local-scope
  load-inputs
  # if 'x' is null, return
  return-unless x
  next-node:&:list:_elem <- rest x
  # clear next pointer of 'x'
  *x <- put *x, next:offset, null
  # if 'x' is at the head of 'in', return the new head
  at-head?:bool <- equal x, in
  return-if at-head?, next-node
  # compute prev-node
  prev-node:&:list:_elem <- copy in
  curr:&:list:_elem <- rest prev-node
  {
    return-unless curr
    found?:bool <- equal curr, x
    break-if found?
    prev-node <- copy curr
    curr <- rest curr
  }
  # set its next pointer to skip 'x'
  *prev-node <- put *prev-node, next:offset, next-node
]

scenario removing-from-list [
  local-scope
  list:&:list:num <- push 3, null
  list <- push 4, list
  list <- push 5, list
  run [
    list2:&:list:num <- rest list  # second element
    list <- remove list2, list
    10:bool/raw <- equal list2, null
    # check structure like before
    list2 <- copy list
    11:num/raw <- first list2
    list2 <- rest list2
    12:num/raw <- first list2
    20:&:list:num/raw <- rest list2
  ]
  memory-should-contain [
    10 <- 0  # remove returned non-null
    11 <- 5  # scanning next, skipping deleted element
    12 <- 3
    20 <- 0  # no more elements
  ]
]

scenario removing-from-start-of-list [
  local-scope
  list:&:list:num <- push 3, null
  list <- push 4, list
  list <- push 5, list
  run [
    list <- remove list, list
    # check structure like before
    list2:&:list:num <- copy list
    10:num/raw <- first list2
    list2 <- rest list2
    11:num/raw <- first list2
    20:&:list:num/raw <- rest list2
  ]
  memory-should-contain [
    10 <- 4  # scanning next, skipping deleted element
    11 <- 3
    20 <- 0  # no more elements
  ]
]

scenario removing-from-end-of-list [
  local-scope
  list:&:list:num <- push 3, null
  list <- push 4, list
  list <- push 5, list
  run [
    # delete last element
    list2:&:list:num <- rest list
    list2 <- rest list2
    list <- remove list2, list
    10:bool/raw <- equal list2, null
    # check structure like before
    list2 <- copy list
    11:num/raw <- first list2
    list2 <- rest list2
    12:num/raw <- first list2
    20:&:list:num/raw <- rest list2
  ]
  memory-should-contain [
    10 <- 0  # remove returned non-null
    11 <- 5  # scanning next, skipping deleted element
    12 <- 4
    20 <- 0  # no more elements
  ]
]

scenario removing-from-singleton-list [
  local-scope
  list:&:list:num <- push 3, null
  run [
    list <- remove list, list
    1:num/raw <- deaddress list
  ]
  memory-should-contain [
    1 <- 0  # back to an empty list
  ]
]

# reverse the elements of a list
# (contributed by Caleb Couch)
def reverse list:&:list:_elem temp:&:list:_elem/contained-in:result -> result:&:list:_elem [
  local-scope
  load-inputs
  return-unless list, temp
  object:_elem <- first, list
  list <- rest list
  temp <- push object, temp
  result <- reverse list, temp
]

scenario reverse-list [
  local-scope
  list:&:list:num <- push 1, null
  list <- push 2, list
  list <- push 3, list
  run [
    stash [list:], list
    list <- reverse list
    stash [reversed:], list
  ]
  trace-should-contain [
    app: list: 3 -> 2 -> 1
    app: reversed: 1 -> 2 -> 3
  ]
]

scenario stash-list [
  local-scope
  list:&:list:num <- push 1, null
  list <- push 2, list
  list <- push 3, list
  run [
    stash [list:], list
  ]
  trace-should-contain [
    app: list: 3 -> 2 -> 1
  ]
]

def to-text in:&:list:_elem -> result:text [
  local-scope
  load-inputs
  buf:&:buffer:char <- new-buffer 80
  buf <- to-buffer in, buf
  result <- buffer-to-array buf
]

# variant of 'to-text' which stops printing after a few elements (and so is robust to cycles)
def to-text-line in:&:list:_elem -> result:text [
  local-scope
  load-inputs
  buf:&:buffer:char <- new-buffer 80
  buf <- to-buffer in, buf, 6  # max elements to display
  result <- buffer-to-array buf
]

def to-buffer in:&:list:_elem, buf:&:buffer:char -> buf:&:buffer:char [
  local-scope
  load-inputs
  {
    break-if in
    buf <- append buf, [[]]
    return
  }
  # append in.value to buf
  val:_elem <- get *in, value:offset
  buf <- append buf, val
  # now prepare next
  next:&:list:_elem <- rest in
  nextn:num <- deaddress next
  return-unless next
  buf <- append buf, [ -> ]
  # and recurse
  remaining:num, optional-input-found?:bool <- next-input
  {
    break-if optional-input-found?
    # unlimited recursion
    buf <- to-buffer next, buf
    return
  }
  {
    break-unless remaining
    # limited recursion
    remaining <- subtract remaining, 1
    buf <- to-buffer next, buf, remaining
    return
  }
  # past recursion depth; insert ellipses and stop
  append buf, [...]
]

scenario stash-empty-list [
  local-scope
  x:&:list:num <- copy null
  run [
    stash x
  ]
  trace-should-contain [
    app: []
  ]
]