1 # Helpers for Unicode. 2 # 3 # Mu has no characters, only code points and graphemes. 4 # Code points are the indivisible atoms of text streams. 5 # https://en.wikipedia.org/wiki/Code_point 6 # Graphemes are the smallest self-contained unit of text. 7 # Graphemes may consist of multiple code points. 8 # 9 # Mu graphemes are always represented in utf-8, and they are required to fit 10 # in 4 bytes. 11 # 12 # Mu doesn't currently support combining code points, or graphemes made of 13 # multiple code points. One day we will. 14 # We also don't currently support code points that translate into multiple 15 # or wide graphemes. (In particular, Tab will never be supported.) 16 17 # transliterated from tb_utf8_unicode_to_char in https://github.com/nsf/termbox 18 # https://wiki.tcl-lang.org/page/UTF%2D8+bit+by+bit explains the algorithm 19 # 20 # The day we want to support combining characters, this function will need to 21 # take multiple code points. Or something. 22 fn to-grapheme in: code-point -> _/eax: grapheme { 23 var c/eax: int <- copy in 24 var num-trailers/ecx: int <- copy 0 25 var first/edx: int <- copy 0 26 $to-grapheme:compute-length: { 27 # single byte: just return it 28 compare c, 0x7f 29 { 30 break-if-> 31 var g/eax: grapheme <- copy c 32 return g 33 } 34 # 2 bytes 35 compare c, 0x7ff 36 { 37 break-if-> 38 num-trailers <- copy 1 39 first <- copy 0xc0pre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */# 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()