https://github.com/akkartik/mu/blob/main/shell/global.mu
  1 type global-table {
  2   data: (handle array global)
  3   final-index: int
  4   cursor-index: int
  5 }
  6 
  7 type global {
  8   name: (handle array byte)
  9   input: (handle gap-buffer)
 10   value: (handle cell)
 11   trace: (handle trace)
 12 }
 13 
 14 fn initialize-globals _self: (addr global-table) {
 15   var self/esi: (addr global-table) <- copy _self
 16   compare self, 0
 17   {
 18     break-if-!=
 19     abort "initialize globals"
 20     return
 21   }
 22   var data-ah/eax: (addr handle array global) <- get self, data
 23   populate data-ah, 0x80
 24   initialize-primitives self
 25 }
 26 
 27 fn load-globals in: (addr handle cell), self: (addr global-table) {
 28   draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "loading globals:", 3/fg, 0/bg
 29   var remaining-ah/esi: (addr handle cell) <- copy in
 30   {
 31     var _remaining/eax: (addr cell) <- lookup *remaining-ah
 32     var remaining/ebx: (addr cell) <- copy _remaining
 33     var done?/eax: boolean <- nil? remaining
 34     compare done?, 0/false
 35     break-if-!=
 36     var curr-ah/eax: (addr handle cell) <- get remaining, left
 37     var _curr/eax: (addr cell) <- lookup *curr-ah
 38     var curr/ecx: (addr cell) <- copy _curr
 39     remaining-ah <- get remaining, right
 40     draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, " ", 2/fg 0/bg
 41     var name-ah/eax: (addr handle cell) <- get curr, left
 42     var name/eax: (addr cell) <- lookup *name-ah
 43     var name-data-ah/eax: (addr handle stream byte) <- get name, text-data
 44     var _name-data/eax: (addr stream byte) <- lookup *name-data-ah
 45     var name-data/edx: (addr stream byte) <- copy _name-data
 46     rewind-stream name-data
 47     draw-stream-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, name-data, 3/fg, 0/bg
 48     var value-ah/eax: (addr handle cell) <- get curr, right
 49     var value/eax: (addr cell) <- lookup *value-ah
 50     var value-data-ah/eax: (addr handle stream byte) <- get value, text-data
 51     var _value-data/eax: (addr stream byte) <- lookup *value-data-ah
 52     var value-data/ecx: (addr stream byte) <- copy _value-data
 53     var value-gap-buffer-storage: (handle gap-buffer)
 54     var value-gap-buffer-ah/edi: (addr handle gap-buffer) <- address value-gap-buffer-storage
 55     allocate value-gap-buffer-ah
 56     var value-gap-buffer/eax: (addr gap-buffer) <- lookup *value-gap-buffer-ah
 57     initialize-gap-buffer value-gap-buffer, 0x1000/4KB
 58     load-gap-buffer-from-stream value-gap-buffer, value-data
 59     load-lexical-scope value-gap-buffer-ah, self
 60     loop
 61   }
 62   move-cursor-to-left-margin-of-next-line 0/screen
 63 }
 64 
 65 fn write-globals out: (addr stream byte), _self: (addr global-table) {
 66   var self/esi: (addr global-table) <- copy _self
 67   compare self, 0
 68   {
 69     break-if-!=
 70     abort "write globals"
 71     return
 72   }
 73   write out, "  (globals . (\n"
 74   var data-ah/eax: (addr handle array global) <- get self, data
 75   var data/eax: (addr array global) <- lookup *data-ah
 76   var final-index/edx: (addr int) <- get self, final-index
 77   var curr-index/ecx: int <- copy 1/skip-0
 78   {
 79     compare curr-index, *final-index
 80     break-if->
 81     var curr-offset/ebx: (offset global) <- compute-offset data, curr-index
 82     var curr/ebx: (addr global) <- index data, curr-offset
 83     var curr-value-ah/edx: (addr handle cell) <- get curr, value
 84     var curr-value/eax: (addr cell) <- lookup *curr-value-ah
 85     var curr-type/eax: (addr int) <- get curr-value, type
 86     {
 87       compare *curr-type, 4/primitive-function
 88       break-if-=
 89       compare *curr-type, 5/screen
 90       break-if-=
 91       compare *curr-type, 6/keyboard
 92       break-if-=
 93       compare *curr-type, 3/stream  # not implemented yet
 94       break-if-=
 95       write out, "    ("
 96       var curr-name-ah/eax: (addr handle array byte) <- get curr, name
 97       var curr-name/eax: (addr array byte) <- lookup *curr-name-ah
 98       write out, curr-name
 99       write out, " . ["
100       var curr-input-ah/eax: (addr handle gap-buffer) <- get curr, input
101       var curr-input/eax: (addr gap-buffer) <- lookup *curr-input-ah
102       {
103         compare curr-input, 0
104         break-if-!=
105         abort "null gap buffer"
106       }
107       append-gap-buffer curr-input, out
108       write out, "])\n"
109     }
110     curr-index <- increment
111     loop
112   }
113   write out, "  ))\n"
114 }
115 
116 # globals layout: 1 char padding, 41 code, 1 padding, 41 code, 1 padding =  85 chars
117 fn render-globals screen: (addr screen), _self: (addr global-table), show-cursor?: boolean {
118   clear-rect screen, 0/xmin, 0/ymin, 0x55/xmax, 0x2f/ymax=screen-height-without-menu, 0xdc/bg=green-bg
119   var self/esi: (addr global-table) <- copy _self
120   compare self, 0
121   {
122     break-if-!=
123     abort "render globals"
124     return
125   }
126   var data-ah/eax: (addr handle array global) <- get self, data
127   var data/eax: (addr array global) <- lookup *data-ah
128   var curr-index/edx: int <- copy 1
129   {
130     var curr-offset/ebx: (offset global) <- compute-offset data, curr-index
131     var curr/ebx: (addr global) <- index data, curr-offset
132     var continue?/eax: boolean <- primitive-global? curr
133     compare continue?, 0/false
134     break-if-=
135     curr-index <- increment
136     loop
137   }
138   var lowest-index/edi: int <- copy curr-index
139   var cursor-index/edx: (addr int) <- get self, cursor-index
140   var curr-index/edx: int <- copy *cursor-index
141   var y1: int
142   copy-to y1, 1/padding-top
143   var y2: int
144   copy-to y2, 1/padding-top
145   $render-globals:loop: {
146     compare curr-index, lowest-index
147     break-if-<
148     {
149       compare y1, 0x2f/ymax
150       break-if-<
151       compare y2, 0x2f/ymax
152       break-if-<
153       break $render-globals:loop
154     }
155     {
156       var cursor-in-current-line?: boolean
157       {
158         compare show-cursor?, 0/false
159         break-if-=
160         var cursor-index/eax: (addr int) <- get self, cursor-index
161         compare *cursor-index, curr-index
162         break-if-!=
163         copy-to cursor-in-current-line?, 1/true
164       }
165       var curr-offset/edx: (offset global) <- compute-offset data, curr-index
166       var curr/edx: (addr global) <- index data, curr-offset
167       var curr-input-ah/eax: (addr handle gap-buffer) <- get curr, input
168       var _curr-input/eax: (addr gap-buffer) <- lookup *curr-input-ah
169       var curr-input/ebx: (addr gap-buffer) <- copy _curr-input
170       compare curr-input, 0
171       break-if-=
172       var curr-trace-ah/eax: (addr handle trace) <- get curr, trace
173       var _curr-trace/eax: (addr trace) <- lookup *curr-trace-ah
174       var curr-trace/edx: (addr trace) <- copy _curr-trace
175       $render-globals:render-global: {
176         var x/eax: int <- copy 0
177         var y/ecx: int <- copy y1
178         compare y, y2
179         {
180           break-if->=
181           x, y <- render-gap-buffer-wrapping-right-then-down screen, curr-input, 1/padding-left, y1, 0x2a/xmax, 0x2f/ymax, cursor-in-current-line?, 7/fg=definition, 0xc5/bg=blue-bg
182           y <- increment
183           y <- render-trace screen, curr-trace, 1/padding-left, y, 0x2a/xmax, 0x2f/ymax, 0/no-cursor
184           y <- increment
185           copy-to y1, y
186           break $render-globals:render-global
187         }
188         x, y <- render-gap-buffer-wrapping-right-then-down screen, curr-input, 0x2b/xmin, y2, 0x54/xmax, 0x2f/ymax, cursor-in-current-line?, 7/fg=definition, 0xc5/bg=blue-bg
189         y <- increment
190         y <- render-trace screen, curr-trace, 0x2b/xmin, y, 0x54/xmax, 0x2f/ymax, 0/no-cursor
191         y <- increment
192         copy-to y2, y
193       }
194     }
195     curr-index <- decrement
196     loop
197   }
198   # render primitives on top
199   render-primitives screen, 1/xmin=padding-left, 0x55/xmax, 0x2f/ymax
200 }
201 
202 fn render-globals-menu screen: (addr screen), _self: (addr global-table) {
203   var _width/eax: int <- copy 0
204   var height/ecx: int <- copy 0
205   _width, height <- screen-size screen
206   var width/edx: int <- copy _width
207   var y/ecx: int <- copy height
208   y <- decrement
209   var height/ebx: int <- copy y
210   height <- increment
211   clear-rect screen, 0/x, y, width, height, 0xc5/bg=blue-bg
212   set-cursor-position screen, 0/x, y
213   draw-text-rightward-from-cursor screen, " ^r ", width, 0/fg, 0x5c/bg=menu-highlight
214   draw-text-rightward-from-cursor screen, " run main  ", width, 7/fg, 0xc5/bg=blue-bg
215   draw-text-rightward-from-cursor screen, " ^s ", width, 0/fg, 0x5c/bg=menu-highlight
216   draw-text-rightward-from-cursor screen, " run sandbox  ", width, 7/fg, 0xc5/bg=blue-bg
217   draw-text-rightward-from-cursor screen, " ^g ", width, 0/fg, 0x5c/bg=menu-highlight
218   draw-text-rightward-from-cursor screen, " go to  ", width, 7/fg, 0xc5/bg=blue-bg
219   draw-text-rightward-from-cursor screen, " ^a ", width, 0/fg, 0x5c/bg=menu-highlight
220   draw-text-rightward-from-cursor screen, " <<  ", width, 7/fg, 0xc5/bg=blue-bg
221   draw-text-rightward-from-cursor screen, " ^b ", width, 0/fg, 0x5c/bg=menu-highlight
222   draw-text-rightward-from-cursor screen, " <word  ", width, 7/fg, 0xc5/bg=blue-bg
223   draw-text-rightward-from-cursor screen, " ^f ", width, 0/fg, 0x5c/bg=menu-highlight
224   draw-text-rightward-from-cursor screen, " word>  ", width, 7/fg, 0xc5/bg=blue-bg
225   draw-text-rightward-from-cursor screen, " ^e ", width, 0/fg, 0x5c/bg=menu-highlight
226   draw-text-rightward-from-cursor screen, " >>  ", width, 7/fg, 0xc5/bg=blue-bg
227 }
228 
229 fn edit-globals _self: (addr global-table), key: grapheme {
230   var self/esi: (addr global-table) <- copy _self
231   # ctrl-s
232   {
233     compare key, 0x13/ctrl-s
234     break-if-!=
235     #
236     refresh-cursor-definition self
237     return
238   }
239   var cursor-index-addr/ecx: (addr int) <- get self, cursor-index
240   var cursor-index/ecx: int <- copy *cursor-index-addr
241   var data-ah/eax: (addr handle array global) <- get self, data
242   var data/eax: (addr array global) <- lookup *data-ah
243   var cursor-offset/ecx: (offset global) <- compute-offset data, cursor-index
244   var curr-global/eax: (addr global) <- index data, cursor-offset
245   var curr-editor-ah/eax: (addr handle gap-buffer) <- get curr-global, input
246   var curr-editor/eax: (addr gap-buffer) <- lookup *curr-editor-ah
247   edit-gap-buffer curr-editor, key
248 }
249 
250 fn create-empty-global _self: (addr global-table), name-stream: (addr stream byte), capacity: int {
251   var self/esi: (addr global-table) <- copy _self
252   var final-index-addr/ecx: (addr int) <- get self, final-index
253   increment *final-index-addr
254   var curr-index/ecx: int <- copy *final-index-addr
255   var cursor-index-addr/eax: (addr int) <- get self, cursor-index
256   copy-to *cursor-index-addr, curr-index
257   var data-ah/eax: (addr handle array global) <- get self, data
258   var data/eax: (addr array global) <- lookup *data-ah
259   var curr-offset/ecx: (offset global) <- compute-offset data, curr-index
260   var curr/esi: (addr global) <- index data, curr-offset
261   var curr-name-ah/eax: (addr handle array byte) <- get curr, name
262   stream-to-array name-stream, curr-name-ah
263   var curr-input-ah/eax: (addr handle gap-buffer) <- get curr, input
264   allocate curr-input-ah
265   var curr-input/eax: (addr gap-buffer) <- lookup *curr-input-ah
266   initialize-gap-buffer curr-input, capacity
267   var trace-ah/eax: (addr handle trace) <- get curr, trace
268
# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

if __name__ == '__main__': from __init__ import init; init()

from os.path import realpath, join, dirname

from ranger import fsobject
from ranger.fsobject.file import File
from ranger.fsobject.directory import Directory
from ranger.shared.settings import SettingsAware

SettingsAware._setup()

TESTDIR = realpath(join(dirname(__file__), 'testdir'))
TESTFILE = join(TESTDIR, 'testfile5234148')
NONEXISTANT_DIR = join(TESTDIR, 'nonexistant')

import unittest
class Test1(unittest.TestCase):
	def test_initial_condition(self):
		# Check for the expected initial condition
		dir = Directory(TESTDIR)

		self.assertEqual(dir.path, TESTDIR)
		self.assertFalse(dir.content_loaded)
		self.assertEqual(dir.filenames, None)
		self.assertEqual(dir.files, None)
		self.assertRaises(fsobject.NotLoadedYet, len, dir)

	def test_after_content_loaded(self):
		import os
		# Check whether the directory has the correct list of filenames.
		dir = Directory(TESTDIR)
		dir.load_content()

		self.assertTrue(dir.exists)
		self.assertEqual(type(dir.filenames), list)

		# Get the filenames you expect it to have and sort both before
		# comparing. I don't expect any order after only loading the filenames.
		assumed_filenames = os.listdir(TESTDIR)
		assumed_filenames = list(map(lambda str: os.path.join(TESTDIR, str),
			assumed_filenames))
		assumed_filenames.sort()
		dir.filenames.sort()

		self.assertTrue(len(dir) > 0)
		self.assertEqual(dir.filenames, assumed_filenames)

		# build a file object for each file in the list assumed_filenames
		# and find exactly one equivalent in dir.files
		for name in assumed_filenames:
			f = File(name)
			f.load()
			for dirfile in dir.files:
				if (f.path == dirfile.path and f.stat == dirfile.stat):
					break
			else:
				self.fail("couldn't find file {0}".format(name))

	def test_nonexistant_dir(self):
		dir = Directory(NONEXISTANT_DIR)
		dir.load_content()
		
		self.assertTrue(dir.content_loaded)
		self.assertFalse(dir.exists)
		self.assertFalse(dir.accessible)
		self.assertEqual(dir.filenames, None)
		self.assertRaises(fsobject.NotLoadedYet, len, dir)

	def test_load_if_outdated(self):
		import os
		import time
		# modify the directory. If the time between the last modification
		# was within the filesystems resolution of mtime, we should have a reload

		def modify_dir():
			open(TESTFILE, 'w').close()
			os.unlink(TESTFILE)

		def mtime():
			return os.stat(TESTDIR).st_mtime

		dir = Directory(TESTDIR)
		dir.load()

		# If the modification happens to be in the same second as the
		# last modification, it will result in mtime having the same
		# integer value. So we wait until the resolution is exceeded
		# and mtime differs.
		old_mtime = mtime()
		for i in range(50):
			modify_dir()
			if old_mtime != mtime(): break
			time.sleep(0.1)
		else:
			# fail after 5 seconds of trying
			self.fail(
					"Cannot perform test: mtime of TESTDIR is not being updated.")

		self.assertTrue(dir.load_if_outdated())

if __name__ == '__main__':
	unittest.main()
: int { 403 var globals/esi: (addr global-table) <- copy _globals 404 compare globals, 0 405 { 406 break-if-!= 407 return -1/not-found 408 } 409 var global-data-ah/eax: (addr handle array global) <- get globals, data 410 var global-data/eax: (addr array global) <- lookup *global-data-ah 411 var final-index/ecx: (addr int) <- get globals, final-index 412 var curr-index/ecx: int <- copy *final-index 413 { 414 compare curr-index, 0 415 break-if-< 416 var curr-offset/ebx: (offset global) <- compute-offset global-data, curr-index 417 var curr/ebx: (addr global) <- index global-data, curr-offset 418 var curr-name-ah/eax: (addr handle array byte) <- get curr, name 419 var curr-name/eax: (addr array byte) <- lookup *curr-name-ah 420 var found?/eax: boolean <- stream-data-equal? sym-name, curr-name 421 compare found?, 0/false 422 { 423 break-if-= 424 return curr-index 425 } 426 curr-index <- decrement 427 loop 428 } 429 return -1/not-found 430 } 431 432 # return the index in globals containing 'sym' 433 # or -1 if not found 434 fn find-symbol-name-in-globals _globals: (addr global-table), sym-name: (addr array byte) -> _/ecx: int { 435 var globals/esi: (addr global-table) <- copy _globals 436 compare globals, 0 437 { 438 break-if-!= 439 return -1/not-found 440 } 441 var global-data-ah/eax: (addr handle array global) <- get globals, data 442 var global-data/eax: (addr array global) <- lookup *global-data-ah 443 var final-index/ecx: (addr int) <- get globals, final-index 444 var curr-index/ecx: int <- copy *final-index 445 { 446 compare curr-index, 0 447 break-if-< 448 var curr-offset/ebx: (offset global) <- compute-offset global-data, curr-index 449 var curr/ebx: (addr global) <- index global-data, curr-offset 450 var curr-name-ah/eax: (addr handle array byte) <- get curr, name 451 var curr-name/eax: (addr array byte) <- lookup *curr-name-ah 452 var found?/eax: boolean <- string-equal? sym-name, curr-name 453 compare found?, 0/false 454 { 455 break-if-= 456 return curr-index 457 } 458 curr-index <- decrement 459 loop 460 } 461 return -1/not-found 462 } 463 464 fn mutate-binding-in-globals name: (addr stream byte), val: (addr handle cell), _globals: (addr global-table), trace: (addr trace) { 465 var globals/esi: (addr global-table) <- copy _globals 466 { 467 compare globals, 0 468 break-if-= 469 var curr-index/ecx: int <- find-symbol-in-globals globals, name 470 compare curr-index, -1/not-found 471 break-if-= 472 var global-data-ah/eax: (addr handle array global) <- get globals, data 473 var global-data/eax: (addr array global) <- lookup *global-data-ah 474 var curr-offset/ebx: (offset global) <- compute-offset global-data, curr-index 475 var curr/ebx: (addr global) <- index global-data, curr-offset 476 var dest/eax: (addr handle cell) <- get curr, value 477 copy-object val, dest 478 return 479 } 480 # otherwise error "unbound symbol: ", sym 481 var stream-storage: (stream byte 0x40) 482 var stream/ecx: (addr stream byte) <- address stream-storage 483 write stream, "unbound symbol: " 484 rewind-stream name 485 write-stream stream, name 486 error-stream trace, stream 487 } 488 489 fn stash-gap-buffer-to-globals _globals: (addr global-table), definitions: (addr stream int), gap: (addr handle gap-buffer) { 490 var globals/eax: (addr global-table) <- copy _globals 491 compare globals, 0 492 { 493 break-if-!= 494 return 495 } 496 var global-data-ah/eax: (addr handle array global) <- get globals, data 497 var global-data/eax: (addr array global) <- lookup *global-data-ah 498 rewind-stream definitions 499 { 500 { 501 var done?/eax: boolean <- stream-empty? definitions 502 compare done?, 0/false 503 } 504 break-if-!= 505 var index: int 506 var index-addr/ecx: (addr int) <- address index 507 read-from-stream definitions, index-addr 508 var index/ecx: int <- copy *index-addr 509 var offset/ebx: (offset global) <- compute-offset global-data, index 510 var dest-global/eax: (addr global) <- index global-data, offset 511 var dest-ah/eax: (addr handle gap-buffer) <- get dest-global, input 512 copy-object gap, dest-ah 513 loop 514 } 515 } 516 517 fn is-definition? _expr: (addr cell) -> _/eax: boolean { 518 var expr/eax: (addr cell) <- copy _expr 519 # if expr->left is neither "define" nor "set", return 520 var left-ah/eax: (addr handle cell) <- get expr, left 521 var _left/eax: (addr cell) <- lookup *left-ah 522 var left/ecx: (addr cell) <- copy _left 523 { 524 var def?/eax: boolean <- symbol-equal? left, "define" 525 compare def?, 0/false 526 break-if-= 527 return 1/true 528 } 529 { 530 var set?/eax: boolean <- symbol-equal? left, "set" 531 compare set?, 0/false 532 break-if-= 533 return 1/true 534 } 535 return 0/false 536 } 537 538 # load all bindings in a single lexical scope, aka gap buffer of the environment, aka file of the file system 539 fn load-lexical-scope in-ah: (addr handle gap-buffer), _globals: (addr global-table) { 540 var globals/esi: (addr global-table) <- copy _globals 541 var definitions-created-storage: (stream int 0x10) 542 var definitions-created/ebx: (addr stream int) <- address definitions-created-storage 543 var trace-h: (handle trace) 544 var trace-ah/edx: (addr handle trace) <- address trace-h 545 allocate trace-ah 546 var trace/eax: (addr trace) <- lookup *trace-ah 547 initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible 548 var dummy-result-h: (handle cell) 549 var dummy-result-ah/ecx: (addr handle cell) <- address dummy-result-h 550 read-and-evaluate-and-save-gap-buffer-to-globals in-ah, dummy-result-ah, globals, definitions-created, trace, 0/no-inner-screen-var, 0/no-inner-keyboard-var 551 # 552 # save trace to all needed globals as well 553 rewind-stream definitions-created 554 var globals-data-ah/eax: (addr handle array global) <- get globals, data 555 var _globals-data/eax: (addr array global) <- lookup *globals-data-ah 556 var globals-data/edi: (addr array global) <- copy _globals-data 557 { 558 var no-definitions?/eax: boolean <- stream-empty? definitions-created 559 compare no-definitions?, 0/false 560 break-if-!= 561 var curr-index: int 562 var curr-index-a/eax: (addr int) <- address curr-index 563 read-from-stream definitions-created, curr-index-a 564 var curr-offset/eax: (offset global) <- compute-offset globals-data, curr-index 565 var curr-global/ecx: (addr global) <- index globals-data, curr-offset 566 var curr-trace-ah/eax: (addr handle trace) <- get curr-global, trace 567 copy-object trace-ah, curr-trace-ah 568 loop 569 } 570 } 571 572 fn set-global-cursor-index _globals: (addr global-table), name-gap: (addr gap-buffer) { 573 var globals/esi: (addr global-table) <- copy _globals 574 var name-storage: (stream byte 0x40) 575 var name/ecx: (addr stream byte) <- address name-storage 576 emit-gap-buffer name-gap, name 577 var index/ecx: int <- find-symbol-in-globals globals, name 578 var dest/edi: (addr int) <- get globals, cursor-index 579 copy-to *dest, index 580 }