summary refs log tree commit diff stats
path: root/tests/effects
Commit message (Expand)AuthorAgeFilesLines
* do not track 'raise Defect' in the .raises: [] clause anymore (#14298)Andreas Rumpf2020-05-111-2/+2
* Error -> Defect for defects (#13908)Jacek Sieka2020-04-281-2/+2
* Add tests for #8481, #6490 and #4061 (#14083)Clyybber2020-04-231-0/+22
* '.push raises: []' now also affects proc types (#13776)Andreas Rumpf2020-03-291-2/+21
* better error messages for Nim's effect systemAraq2020-03-221-1/+2
* fixes a critical GC safety inference bug (#10615)Andreas Rumpf2019-03-051-0/+21
* fixes #6955Andreas Rumpf2019-02-131-1/+10
* Fix handling of reraise in effect tracking (#10582)LemonBoy2019-02-071-0/+12
* system refactorings (#10559)Andreas Rumpf2019-02-061-2/+2
* Fix exception tracking in try blocks (#10455)LemonBoy2019-01-271-0/+14
* require errormsg to be specified before file.Arne Döring2018-12-119-24/+7
* updated tests to be executedArne Döring2018-11-231-0/+5
* make tests green againAndreas Rumpf2018-07-053-4/+4
* make tests green againAndreas Rumpf2018-07-053-4/+4
* make tests green againAndreas Rumpf2018-05-021-1/+1
* make more tests greenAraq2018-02-101-1/+1
* make tests green againAndreas Rumpf2017-10-151-1/+1
* fixes #5620Araq2017-10-141-0/+12
* side-effect computation now done in the proper pass; fixes #4254Andreas Rumpf2016-08-251-3/+3
* tests: Trim .nim files trailing whitespaceAdam Strzelecki2015-09-0410-20/+20
* renamed writeln to writeLine in testspatrick dw2015-06-194-4/+4
* make test greenAraq2015-04-111-1/+1
* Clean up tests/effectsdef2015-02-041-1/+0
* fixes #325Araq2015-01-281-1/+1
* better effect inferenceAraq2014-11-021-1/+1
* made tests greenAraq2014-09-222-2/+3
* more tests greenAraq2014-08-312-8/+8
* fixes 'gcsafe'Araq2014-08-122-1/+17
* make some tests red :) (i.e. print failure details while running the tester)Zahary Karadjov2014-03-161-1/+1
* handle arbitrary expressions dependent on static input params in proc signaturesZahary Karadjov2014-03-161-1/+1
* fixes #584Araq2014-02-141-1/+1
* new tester; all tests categorizedAraq2014-01-1310-0/+198
51'>351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
# 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/>.

import os
from collections import deque
from time import time

from ranger.fsobject import BAD_INFO, File, FileSystemObject
from ranger.shared import SettingsAware
from ranger.ext.accumulator import Accumulator
import ranger.fsobject

def sort_by_basename(path):
	"""returns path.basename (for sorting)"""
	return path.basename

def sort_by_basename_icase(path):
	"""returns case-insensitive path.basename (for sorting)"""
	return path.basename_lower

def sort_by_directory(path):
	"""returns 0 if path is a directory, otherwise 1 (for sorting)"""
	return 1 - path.is_directory

class NoDirectoryGiven(Exception):
	pass

class Directory(FileSystemObject, Accumulator, SettingsAware):
	is_directory = True
	enterable = False
	load_generator = None
	cycle_list = None
	loading = False

	filenames = None
	files = None
	filter = None
	marked_items = None
	scroll_begin = 0
	scroll_offset = 0

	mount_path = '/'
	disk_usage = 0

	last_update_time = -1
	load_content_mtime = -1

	order_outdated = False
	content_outdated = False

	sort_dict = {
		'basename': sort_by_basename,
		'size': lambda path: path.size,
		'mtime': lambda path: -(path.stat and path.stat.st_mtime or 1),
		'type': lambda path: path.mimetype,
	}

	def __init__(self, path):
		from os.path import isfile

		if isfile(path):
			raise NoDirectoryGiven()

		Accumulator.__init__(self)
		FileSystemObject.__init__(self, path)

		self.marked_items = list()

		for opt in ('sort_directories_first', 'sort', 'sort_reverse',
				'sort_case_insensitive'):
			self.settings.signal_bind('setopt.' + opt,
					self.request_resort, weak=True)

		for opt in ('filter', 'hidden_filter', 'show_hidden'):
			self.settings.signal_bind('setopt.' + opt,
				self.request_reload, weak=True)

	def request_resort(self):
		self.order_outdated = True

	def request_reload(self):
		self.content_outdated = True

	def get_list(self):
		return self.files

	def mark_item(self, item, val):
		item._mark(val)
		if val:
			if item in self.files and not item in self.marked_items:
				self.marked_items.append(item)
		else:
			while True:
				try:
					self.marked_items.remove(item)
				except ValueError:
					break

	def toggle_mark(self, item):
		self.mark_item(item, not item.marked)

	def toggle_all_marks(self):
		for item in self.files:
			self.toggle_mark(item)

	def mark_all(self, val):
		for item in self.files:
			self.mark_item(item, val)

		if not val:
			del self.marked_items[:]
			self._clear_marked_items()

	def _gc_marked_items(self):
		for item in list(self.marked_items):
			if item.path not in self.filenames:
				self.marked_items.remove(item)

	def _clear_marked_items(self):
		for item in self.marked_items:
			item._mark(False)
		del self.marked_items[:]

	def get_selection(self):
		"""READ ONLY"""
		self._gc_marked_items()
		if self.marked_items:
			return [item for item in self.files if item.marked]
		elif self.pointed_obj:
			return [self.pointed_obj]
		else:
			return []

	def load_bit_by_bit(self):
		"""
		Returns a generator which load a part of the directory
		in each iteration.
		"""

		# log("generating loader for " + self.path + "(" + str(id(self)) + ")")
		from os.path import join, isdir, basename
		from os import listdir
		import ranger.ext.mount_path

		self.loading = True
		self.load_if_outdated()

		try:
			if self.exists and self.runnable:
				yield
				self.mount_path = ranger.ext.mount_path.mount_path(self.path)

				filenames = []
				for fname in listdir(self.path):
					if not self.settings.show_hidden:
						hfilter = self.settings.hidden_filter
						if hfilter:
							if isinstance(hfilter, str) and hfilter in fname:
								continue
							if hasattr(hfilter, 'search') and \
								hfilter.search(fname):
								continue
					if isinstance(self.filter, str) and self.filter \
							and self.filter not in fname:
						continue
					filenames.append(join(self.path, fname))
				yield

				self.load_content_mtime = os.stat(self.path).st_mtime

				marked_paths = [obj.path for obj in self.marked_items]

				files = []
				for name in filenames:
					if isdir(name):
						try:
							item = self.fm.env.get_directory(name)
						except:
							item = Directory(name)
					else:
						item = File(name)
					item.load_if_outdated()
					files.append(item)
					yield

				self.disk_usage = sum(f.size for f in files if f.is_file)

				self.scroll_offset = 0
				self.filenames = filenames
				self.infostring = ' %d' % len(self.filenames) # update the infostring
				self.files = files

				self._clear_marked_items()
				for item in self.files:
					if item.path in marked_paths:
						self.mark_item(item, True)
					else:
						self.mark_item(item, False)

				self.sort()

				if len(self.files) > 0:
					if self.pointed_obj is not None:
						self.sync_index()
					else:
						self.move(to=0)
			else:
				self.filenames = None
				self.files = None
				self.infostring = BAD_INFO

			self.cycle_list = None
			self.content_loaded = True
			self.last_update_time = time()

		finally:
			self.loading = False

	def unload(self):
		self.loading = False
		self.load_generator = None

	def load_content(self, schedule=None):
		"""
		Loads the contents of the directory. Use this sparingly since
		it takes rather long.
		"""
		self.content_outdated = False

		if not self.loading:
			self.load_once()

			if not self.accessible:
				self.content_loaded = True
				return

			if schedule is None:
				schedule = True   # was: self.size > 30

			if self.load_generator is None:
				self.load_generator = self.load_bit_by_bit()

				if schedule and self.fm:
					self.fm.loader.add(self)
				else:
					for _ in self.load_generator:
						pass
					self.load_generator = None

			elif not schedule or not self.fm:
				for _ in self.load_generator:
					pass
				self.load_generator = None


	def sort(self):
		"""Sort the containing files"""
		if self.files is None:
			return

		old_pointed_obj = self.pointed_obj
		try:
			sort_func = self.sort_dict[self.settings.sort]
		except:
			sort_func = sort_by_basename

		if self.settings.sort_case_insensitive and \
				sort_func == sort_by_basename:
			sort_func = sort_by_basename_icase

		self.files.sort(key = sort_func)

		if self.settings.sort_reverse:
			self.files.reverse()

		if self.settings.sort_directories_first:
			self.files.sort(key = sort_by_directory)

		if self.pointer is not None:
			self.move_to_obj(old_pointed_obj)
		else:
			self.correct_pointer()

	def sort_if_outdated(self):
		"""Sort the containing files if they are outdated"""
		if self.order_outdated:
			self.order_outdated = False
			self.sort()
			return True
		return False

	def move_to_obj(self, arg):
		try:
			arg = arg.path
		except:
			pass
		self.load_content_once(schedule=False)
		if self.empty():
			return

		Accumulator.move_to_obj(self, arg, attr='path')

	def search_fnc(self, fnc, forward=True):
		if not hasattr(fnc, '__call__'):
			return False

		length = len(self)

		if forward:
			generator = ((self.pointer + (x + 1)) % length \
					for x in range(length-1))
		else:
			generator = ((self.pointer - (x + 1)) % length \
					for x in range(length-1))

		for i in generator:
			_file = self.files[i]
			if fnc(_file):
				self.pointer = i
				self.pointed_obj = _file
				self.correct_pointer()
				return True
		return False

	def set_cycle_list(self, lst):
		self.cycle_list = deque(lst)

	def cycle(self, forward=True):
		if self.cycle_list:
			if forward is True:
				self.cycle_list.rotate(-1)
			elif forward is False:
				self.cycle_list.rotate(1)

			self.move_to_obj(self.cycle_list[0])

	def correct_pointer(self):
		"""Make sure the pointer is in the valid range"""
		Accumulator.correct_pointer(self)

		try:
			if self == self.fm.env.cwd:
				self.fm.env.cf = self.pointed_obj
		except:
			pass

	def load_content_once(self, *a, **k):
		"""Load the contents of the directory if not done yet"""
		if not self.content_loaded:
			self.load_content(*a, **k)
			return True
		return False

	def load_content_if_outdated(self, *a, **k):
		"""
		Load the contents of the directory if it's
		outdated or not done yet
		"""

		if self.load_content_once(*a, **k): return True

		if self.files is None or self.content_outdated:
			self.load_content(*a, **k)
			return True

		try:
			real_mtime = os.stat(self.path).st_mtime
		except OSError:
			real_mtime = None
			return False
		if self.stat:
			cached_mtime = self.load_content_mtime
		else:
			cached_mtime = 0

		if real_mtime != cached_mtime:
			self.load_content(*a, **k)
			return True
		return False

	def empty(self):
		"""Is the directory empty?"""
		return self.files is None or len(self.files) == 0

	def __nonzero__(self):
		"""Always True"""
		return True
	__bool__ = __nonzero__

	def __len__(self):
		"""The number of containing files"""
		if not self.accessible or not self.content_loaded:
			raise ranger.fsobject.NotLoadedYet()
		assert self.files is not None
		return len(self.files)

	def __eq__(self, other):
		"""Check for equality of the directories paths"""
		return isinstance(other, Directory) and self.path == other.path

	def __neq__(self, other):
		"""Check for inequality of the directories paths"""
		return not self.__eq__(other)

	def __hash__(self):
		return hash(self.path)