├── tests ├── .gitignore ├── C.htop.md ├── Java.mockito.md ├── Python.flake8.md ├── test_classdiff.py ├── Python.flake8.ranges ├── C.htop.ranges ├── Java.mockito.ranges ├── C.htop.expected.imap_np ├── Java.mockito.expected.imap_np ├── Python.flake8.expected.imap_np ├── C.htop.a.tags ├── C.htop.expected.gv ├── C.htop.b.tags ├── Java.mockito.expected.gv └── Python.flake8.expected.gv ├── .gitignore ├── doc └── classref.ctags ├── setup.py ├── classdiff ├── diff_range.py ├── config.py ├── main.py ├── tag_language.py ├── graphviz.py └── tag_model.py ├── Makefile ├── README.md ├── gitk-cl └── LICENSE /tests/.gitignore: -------------------------------------------------------------------------------- 1 | *.actual.gv 2 | *.png 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tags 2 | __pycache__/ 3 | dist/ 4 | build/ 5 | *.egg-info/ 6 | -------------------------------------------------------------------------------- /tests/C.htop.md: -------------------------------------------------------------------------------- 1 | ``` 2 | git clone https://github.com/hishamhm/htop 3 | gitk-cl 42c3a1f 4 | ``` 5 | 6 | Notable Features: 7 | 8 | * Attribute types 9 | * Structs 10 | * Enum 11 | * Header includes 12 | -------------------------------------------------------------------------------- /tests/Java.mockito.md: -------------------------------------------------------------------------------- 1 | ``` 2 | git clone https://github.com/mockito/mockito 3 | gitk-cl 3d2e721fafa390db 4 | ``` 5 | 6 | Notable Features: 7 | 8 | * Interface 9 | * Realisation 10 | * Methods added, removed, changed 11 | * Class renamed 12 | -------------------------------------------------------------------------------- /tests/Python.flake8.md: -------------------------------------------------------------------------------- 1 | ``` 2 | git clone https://gitlab.com/pycqa/flake8 3 | gitk-cl 3d1a42..ee7081c8 4 | ``` 5 | 6 | Multiple commits between two minor releases 7 | 8 | Notable Features: 9 | 10 | * Mix of global und non-global scope 11 | * Derivation 12 | * File added 13 | * Methods added, changed 14 | * Find the starting class of an association based on line numbers 15 | -------------------------------------------------------------------------------- /doc/classref.ctags: -------------------------------------------------------------------------------- 1 | # Replace java with any language out of `ctags --list-languages`. 2 | # This regex matches up to three words per line. 3 | 4 | --kinddef-java=w,word,Word 5 | --_roledef-java.w=pascalcase,Word in Pascal case 6 | --regex-java=/.*\<([A-Z]+[a-z]+[a-zA-Z0-9_]*)\>/\1/w/{_role=pascalcase} 7 | --regex-java=/.*\<([A-Z]+[a-z]+[a-zA-Z0-9_]*)\>.*\<[A-Z]+[a-z]+[a-zA-Z0-9_]*\>/\1/w/{_role=pascalcase} 8 | --regex-java=/.*\<([A-Z]+[a-z]+[a-zA-Z0-9_]*)\>.*\<[A-Z]+[a-z]+[a-zA-Z0-9_]*\>.*\<[A-Z]+[a-z]+[a-zA-Z0-9_]*\>/\1/w/{_role=pascalcase} 9 | 10 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | 2 | from setuptools import setup 3 | 4 | version = "1.0.0" 5 | 6 | setup( 7 | name="gitk-class-diagram", 8 | version=version, 9 | license="GPLv3+", 10 | description="Class diagrams based on commit diffs", 11 | url="https://github.com/stefandtw/gitk-class-diagram", 12 | packages=["classdiff"], 13 | scripts=["gitk-cl"], 14 | entry_points={ 15 | "console_scripts": [ 16 | "classdiff = classdiff.main:main", 17 | ], 18 | }, 19 | python_requires=">=3.7", 20 | classifiers=[ 21 | "Programming Language :: Python :: 3", 22 | "Programming Language :: Tcl", 23 | "Topic :: Software Development", 24 | "Topic :: Software Development :: Version Control :: Git", 25 | "License :: OSI Approved :: GNU General Public License v3 or later" 26 | " (GPLv3+)" 27 | ], 28 | ) 29 | -------------------------------------------------------------------------------- /classdiff/diff_range.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import re 4 | from classdiff.tag_model import LineRange 5 | 6 | 7 | class DiffRangeFactory: 8 | def fromfile(self, path, model): 9 | if not path or not os.path.isfile(path): 10 | return 11 | with open(path) as rangesfile: 12 | fname = "" 13 | for linecmp in rangesfile: 14 | line = linecmp.rstrip() 15 | if line.startswith("+++ "): 16 | fname = line.split()[-1] 17 | elif fname and fname in model: 18 | self._match(line, model[fname]) 19 | 20 | def _match(self, line, file): 21 | match = re.search(r"^@@@?( \S+){1,2} \+(\d+)(,(\d+))? @@@?", line) 22 | if match: 23 | start = int(match[2]) 24 | count = int(match[4]) if match[3] else 1 25 | r = LineRange(start, start + count - 1) 26 | file.add_diff_range(r) 27 | -------------------------------------------------------------------------------- /classdiff/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | def load_defaults(): 3 | pass 4 | 5 | 6 | Config.show_nested_classes = False 7 | Config.find_rel_scope_using_line_numbers = True 8 | Config.cluster_packages = True 9 | Config.hide_single_file_packages = True 10 | Config.show_attr_type = True 11 | Config.show_op_return_type = False 12 | Config.show_visibility = True 13 | Config.show_single_scope_file_border = False 14 | Config.font = "Roboto, Verdana, Arial" 15 | Config.font_size_classname = 18 16 | Config.font_size_other = 11 17 | Config.bg_removed = "#ffaaaa" 18 | Config.bg_added = "#aaffaa" 19 | Config.bg_changed = "#ddf3ff" 20 | Config.bg_neutral = "#fff3d0" 21 | Config.fg_removed = "#dd0000" 22 | Config.fg_added = "#00dd00" 23 | Config.package_bg = "white" 24 | Config.graph_bg = "white" 25 | Config.file_bg = "white" 26 | Config.package_border = "#777777" 27 | # Higher maxiter produces better layouts, but also takes more time. 28 | Config.maxiter = 600 29 | # Big graphs are slow because of splines=compound. Don't use it if graphs are 30 | # bigger than X. 31 | Config.splines_threshold = 5000 32 | Config.dpi = 96 33 | -------------------------------------------------------------------------------- /tests/test_classdiff.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import subprocess 6 | import sys 7 | import unittest 8 | from unittest import TestCase 9 | from unittest.mock import patch 10 | from classdiff import main 11 | 12 | 13 | class TestGvFiles(TestCase): 14 | 15 | def test_iterate(self): 16 | cases = 0 17 | for exp_gv in os.listdir("tests"): 18 | if not exp_gv.endswith(".expected.gv"): 19 | continue 20 | base = "tests/" + exp_gv.replace(".expected.gv", "") 21 | a = base + ".a.tags" 22 | b = base + ".b.tags" 23 | ranges = base + ".ranges" 24 | expected = base + ".expected.gv" 25 | actual = base + ".actual.gv" 26 | with self.subTest(msg=exp_gv): 27 | with patch.object(sys, "argv", ["classdiff", "-r", ranges, 28 | "-o", actual, a, b]): 29 | main.main() 30 | with open(actual, "r") as f: 31 | act_content = f.read().strip() 32 | with open(expected, "r") as f: 33 | exp_content = f.read().strip() 34 | diffcmd = f"diff -u {actual} {expected} | head -n 15" 35 | diffresult = subprocess.run(diffcmd, check=False, text=True, 36 | stdout=subprocess.PIPE, shell=True) 37 | msg = diffcmd + "\n" + diffresult.stdout 38 | self.assertTrue(act_content == exp_content, msg) 39 | cases += 1 40 | self.assertGreater(cases, 0) 41 | 42 | 43 | if __name__ == "__main__": 44 | unittest.main() 45 | -------------------------------------------------------------------------------- /tests/Python.flake8.ranges: -------------------------------------------------------------------------------- 1 | +++ docs/source/release-notes/3.7.2.rst 2 | @@ -1 +1 @@ 3 | +++ docs/source/release-notes/3.7.3.rst 4 | @@ -0,0 +1,43 @@ 5 | +++ docs/source/release-notes/index.rst 6 | @@ -11,0 +12 @@ with the newest releases first. 7 | +++ src/flake8/__init__.py 8 | @@ -18 +18 @@ LOG.addHandler(logging.NullHandler()) 9 | +++ src/flake8/main/application.py 10 | @@ -8 +7,0 @@ from typing import List, Optional, Sequence # noqa: F401 (until flake8 3.7) 11 | @@ -21 +20 @@ from flake8.plugins import manager as plugin_manager 12 | @@ -22,0 +22,2 @@ if TYPE_CHECKING: 13 | +++ src/flake8/plugins/manager.py 14 | @@ -258,0 +259,6 @@ class PluginManager(object): # pylint: disable=too-few-public-methods 15 | +++ src/flake8/style_guide.py 16 | @@ -340 +340 @@ class StyleGuideManager(object): 17 | @@ -439 +439,3 @@ class StyleGuide(object): 18 | @@ -446 +448 @@ class StyleGuide(object): 19 | @@ -462 +464,3 @@ class StyleGuide(object): 20 | +++ src/flake8/utils.py 21 | @@ -13,2 +13 @@ from typing import List, Pattern, Sequence # noqa: F401 (until flake8 3,7) 22 | @@ -16 +15,3 @@ from typing import Union # noqa: F401 (until flake8 3.7) 23 | @@ -112,0 +114,15 @@ def parse_files_to_codes_mapping(value): # noqa: C901 24 | @@ -126 +142 @@ def parse_files_to_codes_mapping(value): # noqa: C901 25 | @@ -139 +155 @@ def parse_files_to_codes_mapping(value): # noqa: C901 26 | +++ tests/integration/test_main.py 27 | @@ -9 +9 @@ def test_diff_option(tmpdir, capsys): 28 | @@ -43,0 +44,44 @@ index d64ac39..7d943de 100644 29 | +++ tests/unit/test_style_guide.py 30 | @@ -6,0 +7 @@ import pytest 31 | @@ -27,2 +28,5 @@ def test_handle_error_does_not_raise_type_errors(): 32 | @@ -63,3 +67,5 @@ def test_style_guide_applies_to(style_guide_file, filename, expected): 33 | +++ tests/unit/test_utils.py 34 | @@ -6,0 +7 @@ import pytest 35 | @@ -114 +115 @@ def test_invalid_file_list(value): 36 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | GV:=fdp 3 | GITK:=gitk 4 | CTAGS:=ctags 5 | PY:=python3 6 | PIP:=pip 7 | 8 | help: 9 | @echo "make check-depends" 10 | @echo " Check whether all required dependencies are present" 11 | @echo "make install" 12 | @echo " Install the application" 13 | @echo "make install-editable" 14 | @echo " Install the application using links to this source" 15 | @echo " directory." 16 | @echo "make uninstall" 17 | @echo " Delete installed files" 18 | @echo "make test-python" 19 | @echo " Execute tests, which create tests/*.actual.gv files" 20 | @echo "make test-images" 21 | @echo " Generate PNGs for all tests/*.gv files." 22 | @echo " Useful if tests fail, to check differences visually." 23 | @echo "make test-update-expected" 24 | @echo " Use tests/*.actual.gv as future tests/*.expected.gv." 25 | @echo " Compare actual and expected PNGs before you use this." 26 | @echo "make lint" 27 | @echo " Run lint tool" 28 | 29 | check-depends: 30 | @echo "Checking dependencies" 31 | @which $(GITK) 32 | @git version 33 | @$(GV) -V 34 | @$(CTAGS) --version 35 | @$(PY) --version 36 | 37 | test: test-python 38 | 39 | test-python: clean-test-python 40 | $(PY) -m tests.test_classdiff 41 | 42 | clean-test-python: 43 | rm -f tests/*.actual.gv 44 | 45 | tests/%.png: tests/%.gv 46 | $(GV) -Tpng -o $@ $< 47 | 48 | test-images: clean-test-images $(patsubst %.gv,%.png,$(wildcard tests/*.gv)) 49 | 50 | clean-test-images: 51 | rm -f tests/*.png 52 | 53 | test-update-expected: tests/*.actual.gv 54 | $(foreach file,$^,cp $(file) $(patsubst %.actual.gv,%.expected.gv,$(file));) 55 | 56 | install: 57 | $(PIP) install . 58 | 59 | install-editable: 60 | $(PIP) install -e . 61 | rm -f /usr/bin/gitk-cl 62 | ln -s $$(readlink -f gitk-cl) /usr/bin/gitk-cl 63 | 64 | install-from-pypi: 65 | $(PIP) install gitk-class-diagram 66 | 67 | uninstall: 68 | $(PIP) uninstall gitk-class-diagram 69 | 70 | lint: 71 | flake8 72 | 73 | package: 74 | $(PY) setup.py sdist bdist_wheel 75 | 76 | clean-setup: 77 | $(PY) setup.py clean 78 | 79 | .PHONY: check-depends test-python clean-test-python test-images clean-test-images install uninstall lint package clean-setup 80 | -------------------------------------------------------------------------------- /tests/C.htop.ranges: -------------------------------------------------------------------------------- 1 | +++ solaris/Platform.c 2 | @@ -37,0 +38 @@ in the source distribution for its full text. 3 | @@ -39,0 +41,2 @@ in the source distribution for its full text. 4 | @@ -94 +97 @@ const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(Si 5 | +++ solaris/Platform.h 6 | @@ -16,0 +17 @@ in the source distribution for its full text. 7 | @@ -18,0 +20,2 @@ in the source distribution for its full text. 8 | +++ solaris/SolarisProcess.c 9 | @@ -32 +32,2 @@ typedef enum SolarisProcessFields { 10 | @@ -44,0 +46,4 @@ typedef struct SolarisProcess_ { 11 | @@ -70 +75 @@ ProcessFieldData Process_fields[] = { 12 | @@ -98,0 +104 @@ ProcessFieldData Process_fields[] = { 13 | @@ -109,0 +116 @@ ProcessPidColumn Process_pidColumns[] = { 14 | @@ -150,0 +158,9 @@ void SolarisProcess_writeField(Process* this, RichString* str, ProcessField fiel 15 | @@ -180,0 +197,6 @@ long SolarisProcess_compare(const void* v1, const void* v2) { 16 | @@ -189 +211,3 @@ bool Process_isThread(Process* this) { 17 | @@ -191 +215 @@ bool Process_isThread(Process* this) { 18 | @@ -192,0 +217 @@ bool Process_isThread(Process* this) { 19 | +++ solaris/SolarisProcess.h 20 | @@ -24 +24,2 @@ typedef enum SolarisProcessFields { 21 | @@ -36,0 +38,4 @@ typedef struct SolarisProcess_ { 22 | +++ solaris/SolarisProcessList.c 23 | @@ -245,0 +246,138 @@ void ProcessList_delete(ProcessList* this) { 24 | @@ -257 +394,0 @@ void ProcessList_goThroughEntries(ProcessList* this) { 25 | @@ -283 +419,0 @@ void ProcessList_goThroughEntries(ProcessList* this) { 26 | @@ -301,2 +437 @@ void ProcessList_goThroughEntries(ProcessList* this) { 27 | @@ -305,4 +440,8 @@ void ProcessList_goThroughEntries(ProcessList* this) { 28 | @@ -326,2 +465,2 @@ void ProcessList_goThroughEntries(ProcessList* this) { 29 | @@ -337,0 +477,5 @@ void ProcessList_goThroughEntries(ProcessList* this) { 30 | @@ -342 +486,4 @@ void ProcessList_goThroughEntries(ProcessList* this) { 31 | @@ -356,2 +503,2 @@ void ProcessList_goThroughEntries(ProcessList* this) { 32 | @@ -368,2 +515,5 @@ void ProcessList_goThroughEntries(ProcessList* this) { 33 | +++ solaris/SolarisProcessList.h 34 | @@ -53,0 +54,2 @@ void ProcessList_delete(ProcessList* this); 35 | -------------------------------------------------------------------------------- /tests/Java.mockito.ranges: -------------------------------------------------------------------------------- 1 | +++ src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java 2 | @@ -17,0 +18 @@ import net.bytebuddy.implementation.MethodCall; 3 | @@ -32,0 +34 @@ import java.util.ArrayList; 4 | @@ -33,0 +36 @@ import java.util.Random; 5 | @@ -48 +51 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { 6 | @@ -52 +55 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { 7 | @@ -59,0 +63,2 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { 8 | @@ -62 +66,0 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { 9 | @@ -68,0 +73,3 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { 10 | @@ -75,0 +83,2 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { 11 | @@ -124 +133 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { 12 | @@ -127 +136 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { 13 | @@ -130 +139 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { 14 | @@ -133 +142 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { 15 | @@ -135 +144 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { 16 | @@ -180,2 +189,18 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { 17 | @@ -187,3 +212 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { 18 | @@ -202,23 +224,0 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { 19 | @@ -229,6 +229,6 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { 20 | @@ -236 +236,5 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { 21 | @@ -237,0 +242 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { 22 | @@ -325 +330 @@ class SubclassBytecodeGenerator implements BytecodeGenerator { 23 | +++ src/main/java/org/mockito/internal/creation/bytebuddy/SubclassInjectionLoader.java 24 | @@ -50,0 +51,5 @@ class SubclassInjectionLoader implements SubclassLoader { 25 | @@ -70,0 +76,5 @@ class SubclassInjectionLoader implements SubclassLoader { 26 | @@ -102,0 +113,5 @@ class SubclassInjectionLoader implements SubclassLoader { 27 | +++ src/main/java/org/mockito/internal/creation/bytebuddy/SubclassLoader.java 28 | @@ -13,0 +14,7 @@ public interface SubclassLoader { 29 | +++ subprojects/android/src/main/java/org/mockito/android/internal/creation/AndroidLoadingStrategy.java 30 | @@ -17,0 +18,5 @@ class AndroidLoadingStrategy implements SubclassLoader { 31 | +++ /dev/null 32 | @@ -1,244 +0,0 @@ 33 | +++ subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleHandlingTest.java 34 | @@ -0,0 +1,247 @@ 35 | -------------------------------------------------------------------------------- /tests/C.htop.expected.imap_np: -------------------------------------------------------------------------------- 1 | base referer 2 | rect gitk:search_next {solaris/Platform.c} 647,324 844,368 3 | rect gitk:search_next {Platform_setBindings} 647,373 844,392 4 | rect gitk:search_next {Platform_getUptime} 647,392 844,410 5 | rect gitk:search_next {Platform_getLoadAverage} 647,410 844,429 6 | rect gitk:search_next {Platform_getMaxPid} 647,429 844,448 7 | rect gitk:search_next {Platform_setCPUValues} 647,448 844,466 8 | rect gitk:search_next {Platform_setMemoryValues} 647,466 844,485 9 | rect gitk:search_next {Platform_setSwapValues} 647,485 844,504 10 | rect gitk:search_next {Platform_getProcessEnv} 647,504 844,522 11 | rect gitk:search_next {solaris/Platform.h} 508,671 629,715 12 | rect gitk:search_next {solaris/SolarisProcess.c} 186,545 374,589 13 | rect gitk:search_next {SolarisProcess_new} 186,594 374,613 14 | rect gitk:search_next {Process_delete} 186,613 374,631 15 | rect gitk:search_next {SolarisProcess_writeField} 186,631 374,650 16 | rect gitk:search_next {SolarisProcess_compare} 186,650 374,669 17 | rect gitk:search_next {Process_isThread} 186,669 374,687 18 | rect gitk:search_next {SolarisProcessFields} 17,268 249,312 19 | rect gitk:search_next {ZONEID} 17,312 249,331 20 | rect gitk:search_next {ZONE} 17,331 249,350 21 | rect gitk:search_next {PROJID} 17,350 249,368 22 | rect gitk:search_next {TASKID} 17,368 249,387 23 | rect gitk:search_next {POOLID} 17,387 249,406 24 | rect gitk:search_next {CONTID} 17,406 249,424 25 | rect gitk:search_next {LWPID} 17,424 249,443 26 | rect gitk:search_next {LAST_PROCESSFIELD} 17,443 249,462 27 | rect gitk:search_next {SolarisProcess_} 277,264 456,296 28 | rect gitk:search_next {super} 277,296 456,315 29 | rect gitk:search_next {kernel} 277,315 456,334 30 | rect gitk:search_next {zoneid} 277,334 456,352 31 | rect gitk:search_next {zname} 277,352 456,371 32 | rect gitk:search_next {taskid} 277,371 456,390 33 | rect gitk:search_next {projid} 277,390 456,408 34 | rect gitk:search_next {is_lwp} 277,408 456,427 35 | rect gitk:search_next {realpid} 277,427 456,446 36 | rect gitk:search_next {realppid} 277,446 456,464 37 | rect gitk:search_next {lwpid} 277,464 456,483 38 | rect gitk:search_next {solaris/SolarisProcessList.c} 127,19 383,63 39 | rect gitk:search_next {SolarisProcessList_readZoneName} 127,68 383,87 40 | rect gitk:search_next {ProcessList_new} 127,87 383,105 41 | rect gitk:search_next {SolarisProcessList_scanCPUTime} 127,105 383,124 42 | rect gitk:search_next {SolarisProcessList_scanMemoryInfo} 127,124 383,143 43 | rect gitk:search_next {ProcessList_delete} 127,143 383,161 44 | rect gitk:search_next {ProcessList_enumerateLWPs} 127,161 383,180 45 | rect gitk:search_next {ProcessList_goThroughEntries} 127,180 383,199 46 | rect gitk:search_next {CPUData_} 570,48 757,80 47 | rect gitk:search_next {userPercent} 570,80 757,99 48 | rect gitk:search_next {nicePercent} 570,99 757,118 49 | rect gitk:search_next {systemPercent} 570,118 757,136 50 | rect gitk:search_next {irqPercent} 570,136 757,155 51 | rect gitk:search_next {idlePercent} 570,155 757,174 52 | rect gitk:search_next {systemAllPercent} 570,174 757,192 53 | rect gitk:search_next {luser} 570,192 757,211 54 | rect gitk:search_next {lkrnl} 570,211 757,230 55 | rect gitk:search_next {lintr} 570,230 757,248 56 | rect gitk:search_next {lidle} 570,248 757,267 57 | rect gitk:search_next {SolarisProcessList_} 794,195 1013,227 58 | rect gitk:search_next {super} 794,227 1013,246 59 | rect gitk:search_next {kd} 794,246 1013,264 60 | rect gitk:search_next {cpus} 794,264 1013,283 61 | rect gitk:search_next {solaris/SolarisProcess.h} 151,227 323,247 62 | rect gitk:search_next {solaris/SolarisProcessList.h} 691,11 891,31 63 | -------------------------------------------------------------------------------- /tests/Java.mockito.expected.imap_np: -------------------------------------------------------------------------------- 1 | base referer 2 | rect gitk:search_next {SubclassBytecodeGenerator} 17,327 307,356 3 | rect gitk:search_next {CODEGEN_PACKAGE} 17,356 307,373 4 | rect gitk:search_next {MOCKITO_MODULE} 17,373 307,391 5 | rect gitk:search_next {GET_MODULE} 17,391 307,408 6 | rect gitk:search_next {IS_OPEN} 17,408 307,425 7 | rect gitk:search_next {IS_EXPORTED} 17,425 307,443 8 | rect gitk:search_next {CAN_READ} 17,443 307,460 9 | rect gitk:search_next {ADD_EXPORTS} 17,460 307,477 10 | rect gitk:search_next {ADD_READ} 17,477 307,494 11 | rect gitk:search_next {ADD_OPENS} 17,494 307,512 12 | rect gitk:search_next {GET_UNNAMED_MODULE} 17,512 307,529 13 | rect gitk:search_next {SubclassBytecodeGenerator} 17,551 307,568 14 | rect gitk:search_next {mockClass} 17,568 307,586 15 | rect gitk:search_next {addReadEdgeByProbe} 17,586 307,603 16 | rect gitk:search_next {addExportEdgeByProbe} 17,603 307,620 17 | rect gitk:search_next {loadModuleProble} 17,620 307,637 18 | rect gitk:search_next {isGroovyMethod} 17,637 307,655 19 | rect gitk:search_next {nameFor} 17,655 307,672 20 | rect gitk:search_next {isComingFromJDK} 17,672 307,689 21 | rect gitk:search_next {isComingFromSealedPackage} 17,689 307,707 22 | rect gitk:search_next {assertModuleAccessability} 17,707 307,724 23 | rect gitk:search_next {SubclassInjectionLoader} 339,548 592,577 24 | rect gitk:search_next {ERROR_MESSAGE} 339,577 592,594 25 | rect gitk:search_next {loader} 339,594 592,612 26 | rect gitk:search_next {SubclassInjectionLoader} 339,617 592,634 27 | rect gitk:search_next {tryLookup} 339,634 592,651 28 | rect gitk:search_next {isDisrespectingOpenness} 339,651 592,669 29 | rect gitk:search_next {resolveStrategy} 339,669 592,686 30 | rect gitk:search_next {SubclassLoader} 338,435 516,476 31 | rect gitk:search_next {isDisrespectingOpenness} 338,481 516,498 32 | rect gitk:search_next {resolveStrategy} 338,498 516,515 33 | rect gitk:search_next {AndroidLoadingStrategy} 341,333 590,363 34 | rect gitk:search_next {isDisrespectingOpenness} 341,368 590,385 35 | rect gitk:search_next {resolveStrategy} 341,385 590,402 36 | rect gitk:search_next {CanLoadWithSimpleModule} 621,17 1024,47 37 | rect gitk:search_next {can_define_class_in_open_reading_module} 621,51 1024,69 38 | rect gitk:search_next {can_define_class_in_open_reading_private_module} 621,69 1024,86 39 | rect gitk:search_next {can_define_class_in_open_non_reading_module} 621,86 1024,103 40 | rect gitk:search_next {can_define_class_in_open_non_reading_non_exporting_module} 621,103 1024,121 41 | rect gitk:search_next {can_define_class_in_closed_module} 621,121 1024,138 42 | rect gitk:search_next {cannot_define_class_in_non_opened_non_exported_module} 621,138 1024,155 43 | rect gitk:search_next {modularJar} 621,155 1024,172 44 | rect gitk:search_next {type} 621,172 1024,190 45 | rect gitk:search_next {moduleInfo} 621,190 1024,207 46 | rect gitk:search_next {layer} 621,207 1024,224 47 | rect gitk:search_next {ModuleHandlingTest} 146,17 549,47 48 | rect gitk:search_next {can_define_class_in_open_reading_module} 146,51 549,69 49 | rect gitk:search_next {can_define_class_in_open_reading_private_module} 146,69 549,86 50 | rect gitk:search_next {can_define_class_in_open_non_reading_module} 146,86 549,103 51 | rect gitk:search_next {can_define_class_in_open_non_reading_non_exporting_module} 146,103 549,121 52 | rect gitk:search_next {can_define_class_in_closed_module} 146,121 549,138 53 | rect gitk:search_next {cannot_define_class_in_non_opened_non_exported_module} 146,138 549,155 54 | rect gitk:search_next {modularJar} 146,155 549,172 55 | rect gitk:search_next {type} 146,172 549,190 56 | rect gitk:search_next {moduleInfo} 146,190 549,207 57 | rect gitk:search_next {layer} 146,207 549,224 58 | -------------------------------------------------------------------------------- /classdiff/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | from argparse import ArgumentParser 5 | from classdiff.graphviz import GvGraph, GvFile, GvClass, GvRel 6 | from classdiff.tag_model import ModelFactory 7 | from classdiff.diff_range import DiffRangeFactory 8 | from classdiff.config import Config 9 | 10 | 11 | def main(): 12 | Config.load_defaults() 13 | argparser = ArgumentParser(description="Create a class diagram as" 14 | " Graphviz output based on ctags files and diff" 15 | " line ranges") 16 | argparser.add_argument(dest="tagfile_a", help="first tags file or" 17 | " directory", metavar="TAGS1") 18 | argparser.add_argument(dest="tagfile_b", help="second tags file or" 19 | " directory", metavar="TAGS2") 20 | argparser.add_argument("-r", "--ranges", dest="rangesfile", 21 | help="ranges file", metavar="RANGES") 22 | argparser.add_argument("-o", "--output", dest="outfile", help="write" 23 | " output to file OUTPUT", metavar="OUTPUT") 24 | argparser.add_argument("-W", "--width", dest="width", type=int, 25 | help="maximum pixel width of the generated diagram", 26 | metavar="PX", default=10000) 27 | argparser.add_argument("-H", "--height", dest="height", type=int, 28 | help="maximum pixel height of the generated" 29 | " diagram", metavar="PX", default=10000) 30 | argparser.add_argument("-e", "--execute", dest="statements", type=str, 31 | default=[], action="append", metavar="STATEMENT", 32 | help="execute python statement to set config" 33 | " variables") 34 | args = argparser.parse_args() 35 | 36 | if args.statements: 37 | for e in args.statements: 38 | exec(e) 39 | 40 | if args.outfile: 41 | sys.stdout = open(args.outfile, 'w') 42 | 43 | modelfactory = ModelFactory() 44 | model_a = modelfactory.fromfile(args.tagfile_a) 45 | model_b = modelfactory.fromfile(args.tagfile_b) 46 | model = modelfactory.diff(model_a, model_b) 47 | 48 | rangefactory = DiffRangeFactory() 49 | rangefactory.fromfile(args.rangesfile, model) 50 | model.mark_changed_members() 51 | 52 | gr = GvGraph(args.width, args.height) 53 | for fname in model: 54 | file = model[fname] 55 | f = GvFile(file) 56 | gr.add_file(f) 57 | scopes = file.get_scopes() 58 | for scope in scopes: 59 | if (scope.is_global_scope() and scope.is_empty() 60 | and len(scopes) > 1): 61 | continue 62 | cl = GvClass(scope) 63 | f.add_class(cl) 64 | for fname in model: 65 | file = model[fname] 66 | for scope in file.get_scopes(): 67 | for rel in scope.rels: 68 | other = model.get_scope(rel.other_qn) 69 | other_file = model.get_file_by_scope(rel.other_qn) 70 | 71 | def should_rel_use_file(file, scope): 72 | if scope.is_global_scope(): 73 | visible = file.get_scope_count() 74 | if scope.is_empty() and file.get_scope_count() > 1: 75 | visible -= 1 76 | return (visible > 1) 77 | a_use_file = should_rel_use_file(file, scope) 78 | b_use_file = should_rel_use_file(other_file, other) 79 | gvrel = GvRel(scope.qualified_name, a_use_file, 80 | other.qualified_name, b_use_file, 81 | rel.diffsym, rel.type) 82 | gr.add_rel(gvrel) 83 | 84 | gr.print() 85 | if args.outfile: 86 | sys.stdout.close() 87 | 88 | 89 | if __name__ == "__main__": 90 | main() 91 | -------------------------------------------------------------------------------- /classdiff/tag_language.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | class TagLanguage: 5 | def __init__(self, lang): 6 | self.lang = lang 7 | 8 | def is_blacklisted(self): 9 | return self.lang in ("XML", "Maven2", "Iniconf") 10 | 11 | def is_scope(self, kind, roles, access, name): 12 | if roles != "def": 13 | return False 14 | if self.lang in ("CSS", "SCSS"): 15 | return False 16 | return kind in ("class", "interface", "struct", "enum") 17 | 18 | def is_attr(self, kind, roles, access, name): 19 | if roles != "def": 20 | return False 21 | if self.lang in ("Asciidoc", "Markdown", "ReStructuredText"): 22 | return kind in ("chapter", "section") 23 | if self.lang in ("CSS", "SCSS"): 24 | return kind in ("class", "selector", "id") 25 | if self.lang in ("HTML", "XML"): 26 | return kind == "id" 27 | if self.lang == "Java": 28 | return kind in ("field", "enumConstant") 29 | if self.lang == "JavaProperties": 30 | return kind == "key" 31 | if self.lang == "Make": 32 | return kind == "target" 33 | if self.lang == "Python": 34 | return kind == "variable" 35 | if self.lang == "YACC": 36 | return kind == "label" 37 | return kind in ("field", "member", "enumerator") 38 | 39 | def is_op(self, kind, roles, access, name): 40 | if roles != "def": 41 | return False 42 | if self.lang == "Python": 43 | return kind == "member" or (kind == "function" and access == 44 | "public") 45 | if self.lang == "JavaScript": 46 | return kind in ("function", "method") \ 47 | and not name.startswith("AnonymousFunction") 48 | if self.lang == "XSLT": 49 | return kind in ("namedTemplate", "matchedTemplate") 50 | return kind in ("function", "func", "method", "procedure", 51 | "subroutine") 52 | 53 | def is_inv_tag(self, kind, roles, access, name): 54 | # Expecting this not to be called for attrs and ops 55 | if self.lang == "Python" and roles == "def" and kind != "class": 56 | return False 57 | return True 58 | 59 | def get_scope_stereotype(self, kind, implementation): 60 | if implementation in ("abstract"): 61 | return implementation 62 | if kind in ("enum", "interface"): 63 | return kind 64 | return "" 65 | 66 | def is_static_member(self, kind, implementation): 67 | if self.lang == "JavaScript": 68 | return kind == "function" 69 | return False 70 | 71 | def name(self, original): 72 | if self.lang in ("C", "C++") and original.startswith("__anon"): 73 | return "" 74 | return original 75 | 76 | def typeref(self, original): 77 | if self.lang in ("C", "C++") and "__anon" in original: 78 | return re.sub(r"__anon\w*", "", original) 79 | return original.replace("typename:", "") 80 | 81 | def visibility(self, access): 82 | map = {"public": "+", 83 | "private": "-", 84 | "protected": "#", 85 | "package": "~" 86 | } 87 | if self.lang == "Java" and access == "default": 88 | return "~" 89 | return map[access] if access in map else "" 90 | 91 | def scope(self, original): 92 | if self.lang in ("C", "C++") and original.startswith("__anon"): 93 | return "" 94 | if self.lang in ("Go", "C#"): 95 | return re.sub(r"[^.]+\.?", "", original, count=1) 96 | if self.lang == "ReStructuredText": 97 | return "" 98 | return original 99 | 100 | def split_inherits(self, inherits): 101 | return inherits.split(", ") 102 | 103 | def is_nested(self, scope): 104 | return scope != "-" and scope != "" 105 | 106 | def only_works_with_nesting(self): 107 | if self.lang in ("Ruby"): 108 | return True 109 | return False 110 | 111 | def local_name(self, scope): 112 | return scope.split(".")[-1] 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | gitk-class-diagram 2 | ================== 3 | 4 | Extends gitk to show a class diagram based on all files that were modified in a given commit, highlighting any changes. 5 | 6 | ![java](https://user-images.githubusercontent.com/1097029/72220334-f22dd580-354f-11ea-8f14-5f70c065972e.png)*gitk showing some Java classes from [mockito](https://github.com/mockito/mockito)* 7 | 8 | ![c](https://user-images.githubusercontent.com/1097029/72220351-1984a280-3550-11ea-9e0c-552da0af854e.png)*C may not have classes, but putting together structs, enums, and functions per file can still make a useful diagram ([htop](https://github.com/hishamhm/htop))* 9 | 10 | ![python](https://user-images.githubusercontent.com/1097029/72220353-230e0a80-3550-11ea-8ede-8215c55ae474.png)*Python: comparing two minor releases of [flake8](https://gitlab.com/pycqa/flake8)* 11 | 12 | Usage 13 | ----- 14 | 15 | * Whenever you select a commit, a class diagram is created and displayed on the bottom right 16 | * The diagram can also span multiple commits if you compare two revisions 17 | * If you exclude files with gitk's filter, they will not appear in the diagram either 18 | 19 | Interaction 20 | ----------- 21 | 22 | * Left-click on a class to scroll to its file in gitk's diff view 23 | * Left-click on any text in the diagram to search for it in gitk's diff view 24 | * Options if the image is too big: 25 | * Use the mouse wheel to zoom out 26 | * Hold the left mouse button to move around 27 | * Right-click and select "Open PNG" 28 | 29 | Visual aspects 30 | -------------- 31 | 32 | * Red background indicates a removed file, class, attribute, operation, relationship 33 | * Green background indicates an added file, class, attribute, operation, relationship 34 | * Light blue background indicates a changed operation 35 | * A dashed border indicates that the element is not a real class, but rather the global scope of a file 36 | * If a file contains multiple classes, they are clustered within a dotted border 37 | * A blue border indicates the file that is currently shown in gitk's diff view 38 | 39 | Language support 40 | ---------------- 41 | 42 | How well a language works depends on its support in universal-ctags. See: 43 | 44 | ``` 45 | ctags --list-languages 46 | ctags --list-kinds-full= 47 | ``` 48 | 49 | List of what is recognized in popular languages: 50 | 51 | * **Python** — classes, functions, inheritance, function body changes, references to imported files and classes 52 | * **Java** — classes, functions, inheritance, function body changes, references to imported classes of other packages 53 | * **C** — structs, functions, attribute types, function body changes, references to included files 54 | * **Ruby** — classes, functions 55 | * **JavaScript** — classes, functions 56 | * **C++** — most classes are not recognized (ctags provides tags, but gitk-class-diagram does not handle them well enough) 57 | * **Go** — structs, functions, attribute types 58 | 59 | How to get more associations between classes 60 | -------------------------------------------- 61 | 62 | Put a file like [classref.ctags](doc/classref.ctags) in *$HOME/.ctags.d/* to produce associations based on regular expressions. The example file will instruct ctags to record any words in Java source code starting with an upper-case letter (and containing lower-case letters). These words will automatically be interpreted as references to other classes. This works pretty well with some languages. Of course, it's not as reliable as a parser that understands the language. It's also slower. 63 | 64 | How to add your own language 65 | ---------------------------- 66 | 67 | Use ctags' optlib and create the tag kinds `class`, `field` and `function`. 68 | 69 | Installation 70 | ------------ 71 | 72 | ``` 73 | pip install gitk-class-diagram 74 | ``` 75 | 76 | Your existing gitk binary remains as it is. Start the extended version with: 77 | 78 | ``` 79 | gitk-cl 80 | ``` 81 | 82 | Dependencies 83 | ------------ 84 | 85 | * [graphviz](https://www.graphviz.org/) 86 | * [universal-ctags](https://ctags.io/) 87 | * python >= 3.7 88 | 89 | If you are on Windows, make sure that graphviz, python and ctags are in your PATH. 90 | 91 | Configuration 92 | ------------- 93 | 94 | Open gitk's preferences dialog. At the bottom there is an input field which accepts python code. Copy any settings from [config.py](classdiff/config.py) in there and change the values. 95 | 96 | Unfortunately, due to gitk's saving mechanism, the configuration is lost every time you start the original gitk without the extension. 97 | 98 | -------------------------------------------------------------------------------- /tests/Python.flake8.expected.imap_np: -------------------------------------------------------------------------------- 1 | base referer 2 | rect gitk:search_next {docs/source/release-notes/3.7.3.rst} 145,88 228,112 3 | rect gitk:search_next {3.7.3 -- 2019-01-30} 145,112 228,122 4 | rect gitk:search_next {Bugs Fixed} 145,122 228,132 5 | rect gitk:search_next {docs/source/release-notes/index.rst} 685,653 768,677 6 | rect gitk:search_next {3.x Release Series} 685,677 768,687 7 | rect gitk:search_next {2.x Release Series} 685,687 768,697 8 | rect gitk:search_next {1.x Release Series} 685,697 768,707 9 | rect gitk:search_next {0.x Release Series} 685,707 768,717 10 | rect gitk:search_next {src/flake8/__init__.py} 684,38 794,62 11 | rect gitk:search_next {LOG} 684,62 794,72 12 | rect gitk:search_next {__version__} 684,72 794,82 13 | rect gitk:search_next {__version_info__} 684,82 794,92 14 | rect gitk:search_next {_EXTRA_VERBOSE} 684,92 794,102 15 | rect gitk:search_next {_VERBOSITY_TO_LOG_LEVEL} 684,102 794,112 16 | rect gitk:search_next {LOG_FORMAT} 684,112 794,122 17 | rect gitk:search_next {configure_logging} 684,124 794,134 18 | rect gitk:search_next {src/flake8/main/application.py} 393,413 478,437 19 | rect gitk:search_next {LOG} 393,437 478,447 20 | rect gitk:search_next {Application} 236,307 377,325 21 | rect gitk:search_next {__init__} 236,327 377,337 22 | rect gitk:search_next {parse_preliminary_options_and_args} 236,337 377,347 23 | rect gitk:search_next {exit} 236,347 377,357 24 | rect gitk:search_next {make_config_finder} 236,357 377,367 25 | rect gitk:search_next {find_plugins} 236,367 377,377 26 | rect gitk:search_next {register_plugin_options} 236,377 377,387 27 | rect gitk:search_next {parse_configuration_and_cli} 236,387 377,397 28 | rect gitk:search_next {formatter_for} 236,397 377,407 29 | rect gitk:search_next {make_formatter} 236,407 377,417 30 | rect gitk:search_next {make_guide} 236,417 377,427 31 | rect gitk:search_next {src/flake8/plugins/manager.py} 512,303 588,327 32 | rect gitk:search_next {LOG} 512,327 588,337 33 | rect gitk:search_next {__all__} 512,337 588,347 34 | rect gitk:search_next {NO_GROUP_FOUND} 512,347 588,357 35 | rect gitk:search_next {version_for} 512,359 588,369 36 | rect gitk:search_next {Plugin} 612,387 688,404 37 | rect gitk:search_next {__init__} 612,407 688,417 38 | rect gitk:search_next {__repr__} 612,417 688,427 39 | rect gitk:search_next {to_dictionary} 612,427 688,437 40 | rect gitk:search_next {is_in_a_group} 612,437 688,447 41 | rect gitk:search_next {group} 612,447 688,457 42 | rect gitk:search_next {parameters} 612,457 688,467 43 | rect gitk:search_next {parameter_names} 612,467 688,477 44 | rect gitk:search_next {plugin} 612,477 688,487 45 | rect gitk:search_next {version} 612,487 688,497 46 | rect gitk:search_next {plugin_name} 612,497 688,507 47 | rect gitk:search_next {PluginManager} 712,332 828,349 48 | rect gitk:search_next {__init__} 712,352 828,362 49 | rect gitk:search_next {_load_local_plugins} 712,362 828,372 50 | rect gitk:search_next {_load_entrypoint_plugins} 712,372 828,382 51 | rect gitk:search_next {_load_plugin_from_entrypoint} 712,382 828,392 52 | rect gitk:search_next {map} 712,392 828,402 53 | rect gitk:search_next {versions} 712,402 828,412 54 | rect gitk:search_next {PluginTypeManager} 712,444 831,461 55 | rect gitk:search_next {namespace} 712,461 831,471 56 | rect gitk:search_next {__init__} 712,474 831,484 57 | rect gitk:search_next {__contains__} 712,484 831,494 58 | rect gitk:search_next {__getitem__} 712,494 831,504 59 | rect gitk:search_next {get} 712,504 831,514 60 | rect gitk:search_next {names} 712,514 831,524 61 | rect gitk:search_next {plugins} 712,524 831,534 62 | rect gitk:search_next {_generate_call_function} 712,534 831,544 63 | rect gitk:search_next {load_plugins} 712,544 831,554 64 | rect gitk:search_next {register_plugin_versions} 712,554 831,564 65 | rect gitk:search_next {register_options} 712,564 831,574 66 | rect gitk:search_next {Checkers} 516,465 603,482 67 | rect gitk:search_next {namespace} 516,482 603,492 68 | rect gitk:search_next {checks_expecting} 516,495 603,505 69 | rect gitk:search_next {to_dictionary} 516,505 603,515 70 | rect gitk:search_next {register_options} 516,515 603,525 71 | rect gitk:search_next {ast_plugins} 516,525 603,535 72 | rect gitk:search_next {logical_line_plugins} 516,535 603,545 73 | rect gitk:search_next {physical_line_plugins} 516,545 603,555 74 | rect gitk:search_next {ReportFormatters} 498,396 604,413 75 | rect gitk:search_next {namespace} 498,413 604,423 76 | rect gitk:search_next {src/flake8/style_guide.py} 267,502 353,526 77 | rect gitk:search_next {__all__} 267,526 353,536 78 | rect gitk:search_next {LOG} 267,536 353,546 79 | rect gitk:search_next {_Violation} 267,546 353,556 80 | rect gitk:search_next {lru_cache} 267,559 353,569 81 | rect gitk:search_next {find_noqa} 267,569 353,579 82 | rect gitk:search_next {find_more_specific} 267,579 353,589 83 | rect gitk:search_next {find_first_match} 267,589 353,599 84 | rect gitk:search_next {Selected} 87,639 142,656 85 | rect gitk:search_next {Explicitly} 87,656 142,666 86 | rect gitk:search_next {Implicitly} 87,666 142,676 87 | rect gitk:search_next {Ignored} 87,699 136,716 88 | rect gitk:search_next {Explicitly} 87,716 136,726 89 | rect gitk:search_next {Implicitly} 87,726 136,736 90 | rect gitk:search_next {Decision} 107,489 160,506 91 | rect gitk:search_next {Ignored} 107,506 160,516 92 | rect gitk:search_next {Selected} 107,516 160,526 93 | rect gitk:search_next {Violation} 177,489 248,506 94 | rect gitk:search_next {is_inline_ignored} 177,509 248,519 95 | rect gitk:search_next {is_in} 177,519 248,529 96 | rect gitk:search_next {DecisionEngine} 267,619 376,636 97 | rect gitk:search_next {__init__} 267,639 376,649 98 | rect gitk:search_next {_in_all_selected} 267,649 376,659 99 | rect gitk:search_next {_in_extended_selected} 267,659 376,669 100 | rect gitk:search_next {was_selected} 267,669 376,679 101 | rect gitk:search_next {was_ignored} 267,679 376,689 102 | rect gitk:search_next {more_specific_decision_for} 267,689 376,699 103 | rect gitk:search_next {make_decision} 267,699 376,709 104 | rect gitk:search_next {decision_for} 267,709 376,719 105 | rect gitk:search_next {StyleGuideManager} 127,546 245,563 106 | rect gitk:search_next {__init__} 127,566 245,576 107 | rect gitk:search_next {populate_style_guides_with} 127,576 245,586 108 | rect gitk:search_next {style_guide_for} 127,586 245,596 109 | rect gitk:search_next {processing_file} 127,596 245,606 110 | rect gitk:search_next {handle_error} 127,606 245,616 111 | rect gitk:search_next {add_diff_ranges} 127,616 245,626 112 | rect gitk:search_next {StyleGuide} 150,638 230,655 113 | rect gitk:search_next {__init__} 150,658 230,668 114 | rect gitk:search_next {__repr__} 150,668 230,678 115 | rect gitk:search_next {copy} 150,678 230,688 116 | rect gitk:search_next {processing_file} 150,688 230,698 117 | rect gitk:search_next {applies_to} 150,698 230,708 118 | rect gitk:search_next {should_report_error} 150,708 230,718 119 | rect gitk:search_next {handle_error} 150,718 230,728 120 | rect gitk:search_next {add_diff_ranges} 150,728 230,738 121 | rect gitk:search_next {src/flake8/utils.py} 310,9 458,33 122 | rect gitk:search_next {DIFF_HUNK_REGEXP} 310,33 458,43 123 | rect gitk:search_next {COMMA_SEPARATED_LIST_RE} 310,43 458,53 124 | rect gitk:search_next {LOCAL_PLUGIN_LIST_RE} 310,53 458,63 125 | rect gitk:search_next {_Token} 310,63 458,73 126 | rect gitk:search_next {_CODE} 310,73 458,83 127 | rect gitk:search_next {_FILE} 310,83 458,93 128 | rect gitk:search_next {_COLON} 310,93 458,103 129 | rect gitk:search_next {_COMMA} 310,103 458,113 130 | rect gitk:search_next {_WS} 310,113 458,123 131 | rect gitk:search_next {_EOF} 310,123 458,133 132 | rect gitk:search_next {fnmatch} 310,146 458,156 133 | rect gitk:search_next {parse_comma_separated_list} 310,156 458,166 134 | rect gitk:search_next {parse_files_to_codes_mapping} 310,166 458,176 135 | rect gitk:search_next {normalize_paths} 310,176 458,186 136 | rect gitk:search_next {normalize_path} 310,186 458,196 137 | rect gitk:search_next {stdin_get_value} 310,196 458,206 138 | rect gitk:search_next {parse_unified_diff} 310,206 458,216 139 | rect gitk:search_next {is_windows} 310,216 458,226 140 | rect gitk:search_next {can_run_multiprocessing_on_windows} 310,226 458,236 141 | rect gitk:search_next {is_using_stdin} 310,236 458,246 142 | rect gitk:search_next {tests/integration/test_main.py} 511,210 658,234 143 | rect gitk:search_next {test_diff_option} 511,236 658,246 144 | rect gitk:search_next {test_statistics_option} 511,246 658,256 145 | rect gitk:search_next {test_malformed_per_file_ignores_error} 511,256 658,266 146 | rect gitk:search_next {tests/unit/test_style_guide.py} 10,344 203,368 147 | rect gitk:search_next {PER_FILE_IGNORES_UNPARSED} 10,368 203,378 148 | rect gitk:search_next {create_options} 10,381 203,391 149 | rect gitk:search_next {test_handle_error_does_not_raise_type_errors} 10,391 203,401 150 | rect gitk:search_next {test_style_guide_manager} 10,401 203,411 151 | rect gitk:search_next {test_style_guide_applies_to} 10,411 203,421 152 | rect gitk:search_next {test_style_guide_manager_pre_file_ignores_parsing} 10,421 203,431 153 | rect gitk:search_next {test_style_guide_manager_pre_file_ignores} 10,431 203,441 154 | rect gitk:search_next {test_style_guide_manager_style_guide_for} 10,441 203,451 155 | rect gitk:search_next {tests/unit/test_utils.py} 410,603 614,627 156 | rect gitk:search_next {RELATIVE_PATHS} 410,627 614,637 157 | rect gitk:search_next {SINGLE_FILE_DIFF} 410,637 614,647 158 | rect gitk:search_next {SINGLE_FILE_INFO} 410,647 614,657 159 | rect gitk:search_next {TWO_FILE_DIFF} 410,657 614,667 160 | rect gitk:search_next {TWO_FILE_INFO} 410,667 614,677 161 | rect gitk:search_next {MULTI_FILE_DIFF} 410,677 614,687 162 | rect gitk:search_next {MULTI_FILE_INFO} 410,687 614,697 163 | rect gitk:search_next {test_parse_comma_separated_list} 410,700 614,710 164 | rect gitk:search_next {test_parse_files_to_codes_mapping} 410,710 614,720 165 | rect gitk:search_next {test_invalid_file_list} 410,720 614,730 166 | rect gitk:search_next {test_normalize_path} 410,730 614,740 167 | rect gitk:search_next {test_normalize_paths} 410,740 614,750 168 | rect gitk:search_next {test_is_windows_checks_for_nt} 410,750 614,760 169 | rect gitk:search_next {test_fnmatch} 410,760 614,770 170 | rect gitk:search_next {test_fnmatch_returns_the_default_with_empty_default} 410,770 614,780 171 | rect gitk:search_next {test_filenames_from_a_directory} 410,780 614,790 172 | rect gitk:search_next {test_filenames_from_a_directory_with_a_predicate} 410,790 614,800 173 | rect gitk:search_next {src/flake8/main/application.py} 298,288 416,298 174 | rect gitk:search_next {src/flake8/plugins/manager.py} 606,283 724,294 175 | rect gitk:search_next {src/flake8/style_guide.py} 183,469 281,480 176 | -------------------------------------------------------------------------------- /tests/C.htop.a.tags: -------------------------------------------------------------------------------- 1 | solaris/Platform.c local header Platform.h - - - - C 10 2 | solaris/Platform.c local header Meter.h - - - - C 11 3 | solaris/Platform.c local header CPUMeter.h - - - - C 12 4 | solaris/Platform.c local header MemoryMeter.h - - - - C 13 5 | solaris/Platform.c local header SwapMeter.h - - - - C 14 6 | solaris/Platform.c local header TasksMeter.h - - - - C 15 7 | solaris/Platform.c local header LoadAverageMeter.h - - - - C 16 8 | solaris/Platform.c local header ClockMeter.h - - - - C 17 9 | solaris/Platform.c local header HostnameMeter.h - - - - C 18 10 | solaris/Platform.c local header UptimeMeter.h - - - - C 19 11 | solaris/Platform.c local header SolarisProcess.h - - - - C 20 12 | solaris/Platform.c local header SolarisProcessList.h - - - - C 21 13 | solaris/Platform.c system header sys/types.h - - - - C 23 14 | solaris/Platform.c system header sys/time.h - - - - C 24 15 | solaris/Platform.c system header sys/resource.h - - - - C 25 16 | solaris/Platform.c system header utmpx.h - - - - C 26 17 | solaris/Platform.c system header sys/loadavg.h - - - - C 27 18 | solaris/Platform.c system header string.h - - - - C 28 19 | solaris/Platform.c system header kstat.h - - - - C 29 20 | solaris/Platform.c system header time.h - - - - C 30 21 | solaris/Platform.c system header math.h - - - - C 31 22 | solaris/Platform.c system header sys/var.h - - - - C 32 23 | solaris/Platform.c def variable plat_loadavg typename:double[3] - - - C 45 24 | solaris/Platform.c def variable Platform_signals typename:const SignalItem[] - - - C 47 25 | solaris/Platform.c def variable Platform_numberOfSignals typename:const unsigned int - - - C 92 26 | solaris/Platform.c def variable Platform_defaultFields typename:ProcessField[] - - - C 94 27 | solaris/Platform.c def variable Platform_meterTypes typename:MeterClass * [] - - - C 96 28 | solaris/Platform.c def function Platform_setBindings typename:void - - - C 117 119 29 | solaris/Platform.c def variable Platform_numberOfFields typename:int - - - C 121 30 | solaris/Platform.c def function Platform_getUptime typename:int - - - C 125 139 31 | solaris/Platform.c def function Platform_getLoadAverage typename:void - - - C 141 146 32 | solaris/Platform.c def function Platform_getMaxPid typename:int - - - C 148 162 33 | solaris/Platform.c def function Platform_setCPUValues typename:double - - - C 164 195 34 | solaris/Platform.c def function Platform_setMemoryValues typename:void - - - C 197 203 35 | solaris/Platform.c def function Platform_setSwapValues typename:void - - - C 205 209 36 | solaris/Platform.c def function Platform_getProcessEnv typename:char * - - - C 211 214 37 | solaris/Platform.h def macro HEADER_Platform - - - - C++ 4 4 38 | solaris/Platform.h local header Action.h - - - - C++ 14 39 | solaris/Platform.h local header BatteryMeter.h - - - - C++ 15 40 | solaris/Platform.h local header SignalsPanel.h - - - - C++ 16 41 | solaris/Platform.h system header sys/mkdev.h - - - - C++ 17 42 | solaris/Platform.h def typedef kvar_t struct:var - - - C++ 20 43 | solaris/SolarisProcess.c local header Process.h - - - - C 9 44 | solaris/SolarisProcess.c local header ProcessList.h - - - - C 10 45 | solaris/SolarisProcess.c local header SolarisProcess.h - - - - C 11 46 | solaris/SolarisProcess.c local header Platform.h - - - - C 12 47 | solaris/SolarisProcess.c local header CRT.h - - - - C 13 48 | solaris/SolarisProcess.c system header stdlib.h - - - - C 15 49 | solaris/SolarisProcess.c system header string.h - - - - C 16 50 | solaris/SolarisProcess.c system header unistd.h - - - - C 17 51 | solaris/SolarisProcess.c system header sys/syscall.h - - - - C 18 52 | solaris/SolarisProcess.c def variable SolarisProcess_class typename:ProcessClass - - - C 58 53 | solaris/SolarisProcess.c def variable Process_fields typename:ProcessFieldData[] - - - C 68 54 | solaris/SolarisProcess.c def variable Process_pidColumns typename:ProcessPidColumn[] - - - C 102 55 | solaris/SolarisProcess.c def function SolarisProcess_new typename:SolarisProcess * - - - C 117 122 56 | solaris/SolarisProcess.c def function Process_delete typename:void - - - C 124 129 57 | solaris/SolarisProcess.c def function SolarisProcess_writeField typename:void - - - C 131 156 58 | solaris/SolarisProcess.c def function SolarisProcess_compare typename:long - - - C 158 184 59 | solaris/SolarisProcess.c def function Process_isThread typename:bool - - - C 186 193 60 | solaris/SolarisProcess.h def macro HEADER_SolarisProcess - - - - C++ 4 4 61 | solaris/SolarisProcess.h local header Settings.h - - - - C++ 13 62 | solaris/SolarisProcess.h system header zone.h - - - - C++ 14 63 | solaris/SolarisProcess.h def enum SolarisProcessFields - - - - C++ 16 25 64 | solaris/SolarisProcess.h def enumerator ZONEID - SolarisProcessFields public - - C++ 18 65 | solaris/SolarisProcess.h def enumerator ZONE - SolarisProcessFields public - - C++ 19 66 | solaris/SolarisProcess.h def enumerator PROJID - SolarisProcessFields public - - C++ 20 67 | solaris/SolarisProcess.h def enumerator TASKID - SolarisProcessFields public - - C++ 21 68 | solaris/SolarisProcess.h def enumerator POOLID - SolarisProcessFields public - - C++ 22 69 | solaris/SolarisProcess.h def enumerator CONTID - SolarisProcessFields public - - C++ 23 70 | solaris/SolarisProcess.h def enumerator LAST_PROCESSFIELD - SolarisProcessFields public - - C++ 24 71 | solaris/SolarisProcess.h def typedef SolarisProcessField enum:SolarisProcessFields - - - C++ 25 72 | solaris/SolarisProcess.h def struct SolarisProcess_ - - - - C++ 28 37 73 | solaris/SolarisProcess.h def member super typename:Process SolarisProcess_ public - - C++ 29 74 | solaris/SolarisProcess.h def member kernel typename:int SolarisProcess_ public - - C++ 30 75 | solaris/SolarisProcess.h def member zoneid typename:zoneid_t SolarisProcess_ public - - C++ 31 76 | solaris/SolarisProcess.h def member zname typename:char * SolarisProcess_ public - - C++ 32 77 | solaris/SolarisProcess.h def member taskid typename:taskid_t SolarisProcess_ public - - C++ 33 78 | solaris/SolarisProcess.h def member projid typename:projid_t SolarisProcess_ public - - C++ 34 79 | solaris/SolarisProcess.h def member poolid typename:poolid_t SolarisProcess_ public - - C++ 35 80 | solaris/SolarisProcess.h def member contid typename:ctid_t SolarisProcess_ public - - C++ 36 81 | solaris/SolarisProcess.h def typedef SolarisProcess struct:SolarisProcess_ - - - C++ 37 82 | solaris/SolarisProcess.h def macro Process_isKernelThread - - - - C++ 41 41 83 | solaris/SolarisProcess.h def macro Process_isUserlandThread - - - - C++ 45 45 84 | solaris/SolarisProcessList.c local header ProcessList.h - - - - C 9 85 | solaris/SolarisProcessList.c local header SolarisProcess.h - - - - C 10 86 | solaris/SolarisProcessList.c local header SolarisProcessList.h - - - - C 11 87 | solaris/SolarisProcessList.c system header unistd.h - - - - C 13 88 | solaris/SolarisProcessList.c system header stdlib.h - - - - C 14 89 | solaris/SolarisProcessList.c system header sys/types.h - - - - C 15 90 | solaris/SolarisProcessList.c system header sys/user.h - - - - C 16 91 | solaris/SolarisProcessList.c system header err.h - - - - C 17 92 | solaris/SolarisProcessList.c system header fcntl.h - - - - C 18 93 | solaris/SolarisProcessList.c system header limits.h - - - - C 19 94 | solaris/SolarisProcessList.c system header string.h - - - - C 20 95 | solaris/SolarisProcessList.c system header procfs.h - - - - C 21 96 | solaris/SolarisProcessList.c system header errno.h - - - - C 22 97 | solaris/SolarisProcessList.c system header pwd.h - - - - C 23 98 | solaris/SolarisProcessList.c system header dirent.h - - - - C 24 99 | solaris/SolarisProcessList.c system header math.h - - - - C 25 100 | solaris/SolarisProcessList.c system header time.h - - - - C 26 101 | solaris/SolarisProcessList.c def macro MAXCMDLINE - - - - C 28 28 102 | solaris/SolarisProcessList.c def function SolarisProcessList_readZoneName typename:char * - - - C 65 76 103 | solaris/SolarisProcessList.c def function ProcessList_new typename:ProcessList * - - - C 78 94 104 | solaris/SolarisProcessList.c def function SolarisProcessList_scanCPUTime typename:void - - - C 96 169 105 | solaris/SolarisProcessList.c def function SolarisProcessList_scanMemoryInfo typename:void - - - C 171 236 106 | solaris/SolarisProcessList.c def function ProcessList_delete typename:void - - - C 238 244 107 | solaris/SolarisProcessList.c def function ProcessList_goThroughEntries typename:void - - - C 246 405 108 | solaris/SolarisProcessList.h def macro HEADER_SolarisProcessList - - - - C++ 4 4 109 | solaris/SolarisProcessList.h def macro MAXCMDLINE - - - - C++ 13 13 110 | solaris/SolarisProcessList.h system header kstat.h - - - - C++ 16 111 | solaris/SolarisProcessList.h system header sys/param.h - - - - C++ 17 112 | solaris/SolarisProcessList.h system header zone.h - - - - C++ 18 113 | solaris/SolarisProcessList.h system header sys/uio.h - - - - C++ 19 114 | solaris/SolarisProcessList.h system header sys/resource.h - - - - C++ 20 115 | solaris/SolarisProcessList.h system header sys/sysconf.h - - - - C++ 21 116 | solaris/SolarisProcessList.h system header sys/sysinfo.h - - - - C++ 22 117 | solaris/SolarisProcessList.h system header sys/swap.h - - - - C++ 23 118 | solaris/SolarisProcessList.h def macro ZONE_ERRMSGLEN - - - - C++ 25 25 119 | solaris/SolarisProcessList.h def variable zone_errmsg typename:char[] - - - C++ 26 120 | solaris/SolarisProcessList.h def struct CPUData_ - - - - C++ 28 39 121 | solaris/SolarisProcessList.h def member userPercent typename:double CPUData_ public - - C++ 29 122 | solaris/SolarisProcessList.h def member nicePercent typename:double CPUData_ public - - C++ 30 123 | solaris/SolarisProcessList.h def member systemPercent typename:double CPUData_ public - - C++ 31 124 | solaris/SolarisProcessList.h def member irqPercent typename:double CPUData_ public - - C++ 32 125 | solaris/SolarisProcessList.h def member idlePercent typename:double CPUData_ public - - C++ 33 126 | solaris/SolarisProcessList.h def member systemAllPercent typename:double CPUData_ public - - C++ 34 127 | solaris/SolarisProcessList.h def member luser typename:uint64_t CPUData_ public - - C++ 35 128 | solaris/SolarisProcessList.h def member lkrnl typename:uint64_t CPUData_ public - - C++ 36 129 | solaris/SolarisProcessList.h def member lintr typename:uint64_t CPUData_ public - - C++ 37 130 | solaris/SolarisProcessList.h def member lidle typename:uint64_t CPUData_ public - - C++ 38 131 | solaris/SolarisProcessList.h def typedef CPUData struct:CPUData_ - - - C++ 39 132 | solaris/SolarisProcessList.h def struct SolarisProcessList_ - - - - C++ 41 45 133 | solaris/SolarisProcessList.h def member super typename:ProcessList SolarisProcessList_ public - - C++ 42 134 | solaris/SolarisProcessList.h def member kd typename:kstat_ctl_t * SolarisProcessList_ public - - C++ 43 135 | solaris/SolarisProcessList.h def member cpus typename:CPUData * SolarisProcessList_ public - - C++ 44 136 | solaris/SolarisProcessList.h def typedef SolarisProcessList struct:SolarisProcessList_ - - - C++ 45 137 | -------------------------------------------------------------------------------- /tests/C.htop.expected.gv: -------------------------------------------------------------------------------- 1 | digraph cl { 2 | bgcolor="white"; 3 | size="104.16666666666667,104.16666666666667"; 4 | dpi=96; 5 | outputorder=edgesfirst; 6 | maxiter=600; 7 | splines=compound; 8 | node [shape=none margin=0 style=filled fillcolor="#fff3d0" 9 | fontname="Roboto, Verdana, Arial" fontsize=11]; 10 | edge [arrowhead=open] 11 | subgraph cluster_p_solaris { 12 | label=< 13 |
solaris
>; 14 | bgcolor="white"; color="#777777"; penwidth=5;subgraph cluster_solaris_Platform_c { 15 | style=dotted;penwidth=1;color=black;href="filename:solaris/Platform.c:gitk:scroll_to_file {solaris/Platform.c}" bgcolor="white"; label=< 16 |
Platform.c
>; 17 | style=invis; 18 | label=""; 19 | solaris_Platform_c [label=< 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
Platform.c
Platform_setBindings()
Platform_getUptime()
Platform_getLoadAverage()
Platform_getMaxPid()
Platform_setCPUValues()
Platform_setMemoryValues()
Platform_setSwapValues()
Platform_getProcessEnv()
> ] 30 | } 31 | subgraph cluster_solaris_Platform_h { 32 | style=dotted;penwidth=1;color=black;href="filename:solaris/Platform.h:gitk:scroll_to_file {solaris/Platform.h}" bgcolor="white"; label=< 33 |
Platform.h
>; 34 | style=invis; 35 | label=""; 36 | solaris_Platform_h [label=< 37 | 38 |
Platform.h
> ] 39 | } 40 | subgraph cluster_solaris_SolarisProcess_c { 41 | style=dotted;penwidth=1;color=black;href="filename:solaris/SolarisProcess.c:gitk:scroll_to_file {solaris/SolarisProcess.c}" bgcolor="white"; label=< 42 |
SolarisProcess.c
>; 43 | style=invis; 44 | label=""; 45 | solaris_SolarisProcess_c [label=< 46 | 47 | 48 | 49 | 50 | 51 | 52 |
SolarisProcess.c
SolarisProcess_new()
Process_delete()
SolarisProcess_writeField()
SolarisProcess_compare()
Process_isThread()
> ] 53 | } 54 | subgraph cluster_solaris_SolarisProcess_h { 55 | style=dotted;penwidth=1;color=black;href="filename:solaris/SolarisProcess.h:gitk:scroll_to_file {solaris/SolarisProcess.h}" bgcolor="white"; label=< 56 |
SolarisProcess.h
>; 57 | solaris_SolarisProcess_h_SolarisProcessFields [label=< 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
<<enum>>
SolarisProcessFields
+ZONEID
+ZONE
+PROJID
+TASKID
+POOLID
+CONTID
+LWPID
+LAST_PROCESSFIELD
> ] 68 | solaris_SolarisProcess_h_SolarisProcess_ [label=< 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 |
SolarisProcess_
+super : Process
+kernel : int
+zoneid : zoneid_t
+zname : char *
+taskid : taskid_t
+projid : projid_t
+is_lwp : bool
+realpid : pid_t
+realppid : pid_t
+lwpid : pid_t
...
> ] 82 | } 83 | subgraph cluster_solaris_SolarisProcessList_c { 84 | style=dotted;penwidth=1;color=black;href="filename:solaris/SolarisProcessList.c:gitk:scroll_to_file {solaris/SolarisProcessList.c}" bgcolor="white"; label=< 85 |
SolarisProcessList.c
>; 86 | style=invis; 87 | label=""; 88 | solaris_SolarisProcessList_c [label=< 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 |
SolarisProcessList.c
SolarisProcessList_readZoneName()
ProcessList_new()
SolarisProcessList_scanCPUTime()
SolarisProcessList_scanMemoryInfo()
ProcessList_delete()
ProcessList_enumerateLWPs()
ProcessList_goThroughEntries()
> ] 98 | } 99 | subgraph cluster_solaris_SolarisProcessList_h { 100 | style=dotted;penwidth=1;color=black;href="filename:solaris/SolarisProcessList.h:gitk:scroll_to_file {solaris/SolarisProcessList.h}" bgcolor="white"; label=< 101 |
SolarisProcessList.h
>; 102 | solaris_SolarisProcessList_h_CPUData_ [label=< 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 |
CPUData_
+userPercent : double
+nicePercent : double
+systemPercent : double
+irqPercent : double
+idlePercent : double
+systemAllPercent : double
+luser : uint64_t
+lkrnl : uint64_t
+lintr : uint64_t
+lidle : uint64_t
> ] 115 | solaris_SolarisProcessList_h_SolarisProcessList_ [label=< 116 | 117 | 118 | 119 | 120 |
SolarisProcessList_
+super : ProcessList
+kd : kstat_ctl_t *
+cpus : CPUData *
> ] 121 | } 122 | } 123 | solaris_Platform_c -> solaris_Platform_h [] 124 | solaris_Platform_c -> cluster_solaris_SolarisProcess_h [] 125 | solaris_Platform_c -> cluster_solaris_SolarisProcessList_h [] 126 | solaris_SolarisProcess_c -> cluster_solaris_SolarisProcess_h [] 127 | solaris_SolarisProcess_c -> solaris_Platform_h [] 128 | solaris_SolarisProcessList_c -> cluster_solaris_SolarisProcess_h [] 129 | solaris_SolarisProcessList_c -> cluster_solaris_SolarisProcessList_h [] 130 | } 131 | 132 | -------------------------------------------------------------------------------- /tests/C.htop.b.tags: -------------------------------------------------------------------------------- 1 | solaris/Platform.c local header Platform.h - - - - C 10 2 | solaris/Platform.c local header Meter.h - - - - C 11 3 | solaris/Platform.c local header CPUMeter.h - - - - C 12 4 | solaris/Platform.c local header MemoryMeter.h - - - - C 13 5 | solaris/Platform.c local header SwapMeter.h - - - - C 14 6 | solaris/Platform.c local header TasksMeter.h - - - - C 15 7 | solaris/Platform.c local header LoadAverageMeter.h - - - - C 16 8 | solaris/Platform.c local header ClockMeter.h - - - - C 17 9 | solaris/Platform.c local header HostnameMeter.h - - - - C 18 10 | solaris/Platform.c local header UptimeMeter.h - - - - C 19 11 | solaris/Platform.c local header SolarisProcess.h - - - - C 20 12 | solaris/Platform.c local header SolarisProcessList.h - - - - C 21 13 | solaris/Platform.c system header sys/types.h - - - - C 23 14 | solaris/Platform.c system header sys/time.h - - - - C 24 15 | solaris/Platform.c system header sys/resource.h - - - - C 25 16 | solaris/Platform.c system header utmpx.h - - - - C 26 17 | solaris/Platform.c system header sys/loadavg.h - - - - C 27 18 | solaris/Platform.c system header string.h - - - - C 28 19 | solaris/Platform.c system header kstat.h - - - - C 29 20 | solaris/Platform.c system header time.h - - - - C 30 21 | solaris/Platform.c system header math.h - - - - C 31 22 | solaris/Platform.c system header sys/var.h - - - - C 32 23 | solaris/Platform.c def variable plat_loadavg typename:double[3] - - - C 48 24 | solaris/Platform.c def variable Platform_signals typename:const SignalItem[] - - - C 50 25 | solaris/Platform.c def variable Platform_numberOfSignals typename:const unsigned int - - - C 95 26 | solaris/Platform.c def variable Platform_defaultFields typename:ProcessField[] - - - C 97 27 | solaris/Platform.c def variable Platform_meterTypes typename:MeterClass * [] - - - C 99 28 | solaris/Platform.c def function Platform_setBindings typename:void - - - C 120 122 29 | solaris/Platform.c def variable Platform_numberOfFields typename:int - - - C 124 30 | solaris/Platform.c def function Platform_getUptime typename:int - - - C 128 142 31 | solaris/Platform.c def function Platform_getLoadAverage typename:void - - - C 144 149 32 | solaris/Platform.c def function Platform_getMaxPid typename:int - - - C 151 165 33 | solaris/Platform.c def function Platform_setCPUValues typename:double - - - C 167 198 34 | solaris/Platform.c def function Platform_setMemoryValues typename:void - - - C 200 206 35 | solaris/Platform.c def function Platform_setSwapValues typename:void - - - C 208 212 36 | solaris/Platform.c def function Platform_getProcessEnv typename:char * - - - C 214 217 37 | solaris/Platform.h def macro HEADER_Platform - - - - C++ 4 4 38 | solaris/Platform.h local header Action.h - - - - C++ 14 39 | solaris/Platform.h local header BatteryMeter.h - - - - C++ 15 40 | solaris/Platform.h local header SignalsPanel.h - - - - C++ 16 41 | solaris/Platform.h system header signal.h - - - - C++ 17 42 | solaris/Platform.h system header sys/mkdev.h - - - - C++ 18 43 | solaris/Platform.h def macro kill - - - - C++ 20 20 44 | solaris/Platform.h def typedef kvar_t struct:var - - - C++ 23 45 | solaris/SolarisProcess.c local header Process.h - - - - C 9 46 | solaris/SolarisProcess.c local header ProcessList.h - - - - C 10 47 | solaris/SolarisProcess.c local header SolarisProcess.h - - - - C 11 48 | solaris/SolarisProcess.c local header Platform.h - - - - C 12 49 | solaris/SolarisProcess.c local header CRT.h - - - - C 13 50 | solaris/SolarisProcess.c system header stdlib.h - - - - C 15 51 | solaris/SolarisProcess.c system header string.h - - - - C 16 52 | solaris/SolarisProcess.c system header unistd.h - - - - C 17 53 | solaris/SolarisProcess.c system header sys/syscall.h - - - - C 18 54 | solaris/SolarisProcess.c def variable SolarisProcess_class typename:ProcessClass - - - C 63 55 | solaris/SolarisProcess.c def variable Process_fields typename:ProcessFieldData[] - - - C 73 56 | solaris/SolarisProcess.c def variable Process_pidColumns typename:ProcessPidColumn[] - - - C 108 57 | solaris/SolarisProcess.c def function SolarisProcess_new typename:SolarisProcess * - - - C 124 129 58 | solaris/SolarisProcess.c def function Process_delete typename:void - - - C 131 136 59 | solaris/SolarisProcess.c def function SolarisProcess_writeField typename:void - - - C 138 172 60 | solaris/SolarisProcess.c def function SolarisProcess_compare typename:long - - - C 174 206 61 | solaris/SolarisProcess.c def function Process_isThread typename:bool - - - C 208 218 62 | solaris/SolarisProcess.h def macro HEADER_SolarisProcess - - - - C++ 4 4 63 | solaris/SolarisProcess.h local header Settings.h - - - - C++ 13 64 | solaris/SolarisProcess.h system header zone.h - - - - C++ 14 65 | solaris/SolarisProcess.h def enum SolarisProcessFields - - - - C++ 16 26 66 | solaris/SolarisProcess.h def enumerator ZONEID - SolarisProcessFields public - - C++ 18 67 | solaris/SolarisProcess.h def enumerator ZONE - SolarisProcessFields public - - C++ 19 68 | solaris/SolarisProcess.h def enumerator PROJID - SolarisProcessFields public - - C++ 20 69 | solaris/SolarisProcess.h def enumerator TASKID - SolarisProcessFields public - - C++ 21 70 | solaris/SolarisProcess.h def enumerator POOLID - SolarisProcessFields public - - C++ 22 71 | solaris/SolarisProcess.h def enumerator CONTID - SolarisProcessFields public - - C++ 23 72 | solaris/SolarisProcess.h def enumerator LWPID - SolarisProcessFields public - - C++ 24 73 | solaris/SolarisProcess.h def enumerator LAST_PROCESSFIELD - SolarisProcessFields public - - C++ 25 74 | solaris/SolarisProcess.h def typedef SolarisProcessField enum:SolarisProcessFields - - - C++ 26 75 | solaris/SolarisProcess.h def struct SolarisProcess_ - - - - C++ 29 42 76 | solaris/SolarisProcess.h def member super typename:Process SolarisProcess_ public - - C++ 30 77 | solaris/SolarisProcess.h def member kernel typename:int SolarisProcess_ public - - C++ 31 78 | solaris/SolarisProcess.h def member zoneid typename:zoneid_t SolarisProcess_ public - - C++ 32 79 | solaris/SolarisProcess.h def member zname typename:char * SolarisProcess_ public - - C++ 33 80 | solaris/SolarisProcess.h def member taskid typename:taskid_t SolarisProcess_ public - - C++ 34 81 | solaris/SolarisProcess.h def member projid typename:projid_t SolarisProcess_ public - - C++ 35 82 | solaris/SolarisProcess.h def member poolid typename:poolid_t SolarisProcess_ public - - C++ 36 83 | solaris/SolarisProcess.h def member contid typename:ctid_t SolarisProcess_ public - - C++ 37 84 | solaris/SolarisProcess.h def member is_lwp typename:bool SolarisProcess_ public - - C++ 38 85 | solaris/SolarisProcess.h def member realpid typename:pid_t SolarisProcess_ public - - C++ 39 86 | solaris/SolarisProcess.h def member realppid typename:pid_t SolarisProcess_ public - - C++ 40 87 | solaris/SolarisProcess.h def member lwpid typename:pid_t SolarisProcess_ public - - C++ 41 88 | solaris/SolarisProcess.h def typedef SolarisProcess struct:SolarisProcess_ - - - C++ 42 89 | solaris/SolarisProcess.h def macro Process_isKernelThread - - - - C++ 46 46 90 | solaris/SolarisProcess.h def macro Process_isUserlandThread - - - - C++ 50 50 91 | solaris/SolarisProcessList.c local header ProcessList.h - - - - C 9 92 | solaris/SolarisProcessList.c local header SolarisProcess.h - - - - C 10 93 | solaris/SolarisProcessList.c local header SolarisProcessList.h - - - - C 11 94 | solaris/SolarisProcessList.c system header unistd.h - - - - C 13 95 | solaris/SolarisProcessList.c system header stdlib.h - - - - C 14 96 | solaris/SolarisProcessList.c system header sys/types.h - - - - C 15 97 | solaris/SolarisProcessList.c system header sys/user.h - - - - C 16 98 | solaris/SolarisProcessList.c system header err.h - - - - C 17 99 | solaris/SolarisProcessList.c system header fcntl.h - - - - C 18 100 | solaris/SolarisProcessList.c system header limits.h - - - - C 19 101 | solaris/SolarisProcessList.c system header string.h - - - - C 20 102 | solaris/SolarisProcessList.c system header procfs.h - - - - C 21 103 | solaris/SolarisProcessList.c system header errno.h - - - - C 22 104 | solaris/SolarisProcessList.c system header pwd.h - - - - C 23 105 | solaris/SolarisProcessList.c system header dirent.h - - - - C 24 106 | solaris/SolarisProcessList.c system header math.h - - - - C 25 107 | solaris/SolarisProcessList.c system header time.h - - - - C 26 108 | solaris/SolarisProcessList.c def macro MAXCMDLINE - - - - C 28 28 109 | solaris/SolarisProcessList.c def function SolarisProcessList_readZoneName typename:char * - - - C 65 76 110 | solaris/SolarisProcessList.c def function ProcessList_new typename:ProcessList * - - - C 78 94 111 | solaris/SolarisProcessList.c def function SolarisProcessList_scanCPUTime typename:void - - - C 96 169 112 | solaris/SolarisProcessList.c def function SolarisProcessList_scanMemoryInfo typename:void - - - C 171 236 113 | solaris/SolarisProcessList.c def function ProcessList_delete typename:void - - - C 238 244 114 | solaris/SolarisProcessList.c def function ProcessList_enumerateLWPs typename:void - - - C 246 381 115 | solaris/SolarisProcessList.c def function ProcessList_goThroughEntries typename:void - - - C 384 555 116 | solaris/SolarisProcessList.h def macro HEADER_SolarisProcessList - - - - C++ 4 4 117 | solaris/SolarisProcessList.h def macro MAXCMDLINE - - - - C++ 13 13 118 | solaris/SolarisProcessList.h system header kstat.h - - - - C++ 16 119 | solaris/SolarisProcessList.h system header sys/param.h - - - - C++ 17 120 | solaris/SolarisProcessList.h system header zone.h - - - - C++ 18 121 | solaris/SolarisProcessList.h system header sys/uio.h - - - - C++ 19 122 | solaris/SolarisProcessList.h system header sys/resource.h - - - - C++ 20 123 | solaris/SolarisProcessList.h system header sys/sysconf.h - - - - C++ 21 124 | solaris/SolarisProcessList.h system header sys/sysinfo.h - - - - C++ 22 125 | solaris/SolarisProcessList.h system header sys/swap.h - - - - C++ 23 126 | solaris/SolarisProcessList.h def macro ZONE_ERRMSGLEN - - - - C++ 25 25 127 | solaris/SolarisProcessList.h def variable zone_errmsg typename:char[] - - - C++ 26 128 | solaris/SolarisProcessList.h def struct CPUData_ - - - - C++ 28 39 129 | solaris/SolarisProcessList.h def member userPercent typename:double CPUData_ public - - C++ 29 130 | solaris/SolarisProcessList.h def member nicePercent typename:double CPUData_ public - - C++ 30 131 | solaris/SolarisProcessList.h def member systemPercent typename:double CPUData_ public - - C++ 31 132 | solaris/SolarisProcessList.h def member irqPercent typename:double CPUData_ public - - C++ 32 133 | solaris/SolarisProcessList.h def member idlePercent typename:double CPUData_ public - - C++ 33 134 | solaris/SolarisProcessList.h def member systemAllPercent typename:double CPUData_ public - - C++ 34 135 | solaris/SolarisProcessList.h def member luser typename:uint64_t CPUData_ public - - C++ 35 136 | solaris/SolarisProcessList.h def member lkrnl typename:uint64_t CPUData_ public - - C++ 36 137 | solaris/SolarisProcessList.h def member lintr typename:uint64_t CPUData_ public - - C++ 37 138 | solaris/SolarisProcessList.h def member lidle typename:uint64_t CPUData_ public - - C++ 38 139 | solaris/SolarisProcessList.h def typedef CPUData struct:CPUData_ - - - C++ 39 140 | solaris/SolarisProcessList.h def struct SolarisProcessList_ - - - - C++ 41 45 141 | solaris/SolarisProcessList.h def member super typename:ProcessList SolarisProcessList_ public - - C++ 42 142 | solaris/SolarisProcessList.h def member kd typename:kstat_ctl_t * SolarisProcessList_ public - - C++ 43 143 | solaris/SolarisProcessList.h def member cpus typename:CPUData * SolarisProcessList_ public - - C++ 44 144 | solaris/SolarisProcessList.h def typedef SolarisProcessList struct:SolarisProcessList_ - - - C++ 45 145 | -------------------------------------------------------------------------------- /classdiff/graphviz.py: -------------------------------------------------------------------------------- 1 | import html 2 | import re 3 | from classdiff.tag_model import RelType 4 | from classdiff.config import Config 5 | 6 | 7 | class Gv: 8 | def nodename(original): 9 | return re.sub(r"[^a-zA-Z]", "_", original) 10 | 11 | def clustername(original): 12 | return "cluster_" + Gv.nodename(original) 13 | 14 | def htmlesc(s): 15 | return html.escape(s) 16 | 17 | def bgcolor(diffsym, attr_name="bgcolor"): 18 | if diffsym == "-": 19 | return f" {attr_name}=\"{Config.bg_removed}\"" 20 | elif diffsym == "+": 21 | return f" {attr_name}=\"{Config.bg_added}\"" 22 | elif diffsym == "~": 23 | return f" {attr_name}=\"{Config.bg_changed}\"" 24 | else: 25 | return "" 26 | 27 | def fgcolor(diffsym, attr_name="color"): 28 | if diffsym == "-": 29 | return f" {attr_name}=\"{Config.fg_removed}\"" 30 | elif diffsym == "+": 31 | return f" {attr_name}=\"{Config.fg_added}\"" 32 | else: 33 | return "" 34 | 35 | def search_href(s): 36 | return f"href=\"gitk:search_next {{{Gv.htmlesc(s)}}}\"" 37 | 38 | def file_href(file): 39 | return ("href=\"" 40 | f"filename:{Gv.htmlesc(file)}:" 41 | f"gitk:scroll_to_file {{{Gv.htmlesc(file)}}}\"") 42 | 43 | def px_to_inches(s): 44 | return str(float(s) / Config.dpi) 45 | 46 | 47 | class GvBuilder: 48 | def __init__(self): 49 | self.steps = [] 50 | self.end_steps = [] 51 | 52 | def __enter__(self): 53 | return self 54 | 55 | def __exit__(self, exc_type, exc_value, traceback): 56 | end_step = self.end_steps.pop() 57 | self.steps.append(end_step) 58 | 59 | def build(self): 60 | return "".join(self.steps) 61 | 62 | def text(self, content, end=None): 63 | self.steps.append(content) 64 | if end: 65 | self.end_steps.append(end) 66 | return self 67 | 68 | def digraph(self, name): 69 | return self.text(f"""digraph {name} {{ 70 | bgcolor="{Config.graph_bg}";""", "}\n") 71 | 72 | def cluster(self, name): 73 | return self.text(f"""subgraph {Gv.clustername(name)} {{ 74 | """, "}\n") 75 | 76 | def subgraph(self, name): 77 | return self.text(f"""subgraph {Gv.nodename(name)} {{ 78 | """, "}\n") 79 | 80 | def node(self, name, attr): 81 | return self.text(Gv.nodename(name) + " [label=<", f"> {attr}]\n") 82 | 83 | def edge(self, a, b, attr=""): 84 | return self.text(f"{a} -> {b} [{attr}]\n") 85 | 86 | def html(self, tag_name, attr="", line_break=False): 87 | if attr: 88 | attr = " " + attr 89 | prefix = "\n" if line_break else "" 90 | start = f"{prefix}<{tag_name}{attr}>" 91 | self.steps.append(start) 92 | self.end_steps.append(f"") 93 | return self 94 | 95 | def htmltext(self, content, end=None): 96 | return self.text(Gv.htmlesc(content), end) 97 | 98 | def table(self, attr=""): 99 | return self.html("table", attr) 100 | 101 | def tr(self): 102 | return self.html("tr", line_break=True) 103 | 104 | def td(self, attr=""): 105 | return self.html("td", attr) 106 | 107 | def font(self, attr=""): 108 | return self.html("font", attr) 109 | 110 | def b(self): 111 | return self.html("b") 112 | 113 | def i(self): 114 | return self.html("i") 115 | 116 | def u(self): 117 | return self.html("u") 118 | 119 | 120 | class GvGraph: 121 | def __init__(self, width_inches, height_inches): 122 | self.width_inches = Gv.px_to_inches(width_inches) 123 | self.height_inches = Gv.px_to_inches(height_inches) 124 | self.files = [] 125 | self.rels = [] 126 | self.package_files = {} 127 | 128 | def add_file(self, f): 129 | self.files.append(f) 130 | if f.package not in self.package_files: 131 | self.package_files[f.package] = [] 132 | self.package_files[f.package].append(f) 133 | 134 | def add_rel(self, rel): 135 | self.rels.append(rel) 136 | 137 | def get_rels(self, type): 138 | return [rel for rel in self.rels if rel.type == type] 139 | 140 | def _splines(self): 141 | if len(self.rels)**2 * len(self.files) \ 142 | > Config.splines_threshold: 143 | return "curved" 144 | return "compound" 145 | 146 | def print(self): 147 | with GvBuilder().digraph("cl") as b: 148 | b.text(f""" 149 | size="{self.width_inches},{self.height_inches}"; 150 | dpi={Config.dpi}; 151 | outputorder=edgesfirst; 152 | maxiter={Config.maxiter}; 153 | splines={self._splines()}; 154 | node [shape=none margin=0 style=filled fillcolor="{Config.bg_neutral}" 155 | fontname="{Config.font}" fontsize={Config.font_size_other}]; 156 | edge [arrowhead=open]""") 157 | b.text("\n") 158 | self._print_files(b) 159 | self._print_rels(b) 160 | print(b.build()) 161 | 162 | def _print_files(self, b): 163 | for p, files in self.package_files.items(): 164 | draw_package = Config.cluster_packages and \ 165 | not (len(files) == 1 and Config.hide_single_file_packages) 166 | if draw_package: 167 | with b.cluster("p_" + p): 168 | with b.text(" label=<", ">;\n"): 169 | with b.table("cellspacing=\"0\" cellpadding=\"0\"" 170 | " border=\"0\""): 171 | with b.tr(): 172 | with b.td(Gv.search_href(p)): 173 | b.htmltext(p) 174 | b.text(f" bgcolor=\"{Config.package_bg}\";") 175 | b.text(f" color=\"{Config.package_border}\";") 176 | b.text(f" penwidth=5;") 177 | for f in files: 178 | f.print(b, cut_directory=True) 179 | else: 180 | with b.subgraph("p_" + p): 181 | for f in files: 182 | f.print(b) 183 | 184 | def _print_rels(self, b): 185 | assocs = self.get_rels(RelType.ASSOC) 186 | derivs = self.get_rels(RelType.DERIV) 187 | reals = self.get_rels(RelType.REAL) 188 | for rel in assocs: 189 | rel.print(b) 190 | if derivs or reals: 191 | b.text(" edge [arrowhead=empty];\n") 192 | for rel in derivs: 193 | rel.print(b) 194 | if reals: 195 | b.text(" edge [style=dashed];\n") 196 | for rel in reals: 197 | rel.print(b) 198 | 199 | 200 | class GvFile: 201 | def __init__(self, file): 202 | self.classes = [] 203 | self.name = file.name 204 | self.package = file.package 205 | 206 | def add_class(self, cl): 207 | self.classes.append(cl) 208 | 209 | def print(self, b, cut_directory=False): 210 | label = self.name.rsplit("/", 1)[-1] if cut_directory else self.name 211 | with b.cluster(self.name): 212 | b.text("style=dotted;") 213 | b.text("penwidth=1;") 214 | b.text("color=black;") 215 | b.text(Gv.file_href(self.name)) 216 | b.text(f" bgcolor=\"{Config.file_bg}\";") 217 | with b.text(" label=<", ">;\n"): 218 | with b.table("cellspacing=\"0\" cellpadding=\"0\"" 219 | " border=\"0\""): 220 | with b.tr(): 221 | with b.td(Gv.search_href(self.name)): 222 | b.htmltext(label) 223 | if not Config.show_single_scope_file_border \ 224 | and len(self.classes) == 1: 225 | b.text(" style=invis;\n") 226 | b.text(" label=\"\";\n") 227 | for cl in self.classes: 228 | cl.print(b, cut_directory) 229 | 230 | 231 | class GvClass: 232 | def __init__(self, scope): 233 | self.diffsym = scope.diffsym 234 | self.label = scope.name if scope.name != "" else scope.qualified_name 235 | self.qualified_name = scope.qualified_name 236 | self.attrs = scope.get_attrs() 237 | self.ops = scope.get_ops() 238 | self.stereotype = scope.stereotype 239 | self.fake = scope.is_global_scope() 240 | 241 | def print(self, b, cut_directory): 242 | fillcolor = Gv.bgcolor(self.diffsym, "fillcolor") 243 | style = " style=\"dashed\"" if self.fake else "" 244 | with b.node(self.qualified_name, fillcolor): 245 | with b.table("cellspacing=\"0\" cellpadding=\"1\"" + style): 246 | with b.tr(): 247 | with b.td(Gv.search_href(self.label) + " sides=\"b\""): 248 | self._print_name(b, cut_directory) 249 | self._print_attrs(b) 250 | with b.tr(): 251 | with b.td("sides=\"b\""): 252 | pass 253 | self._print_ops(b) 254 | 255 | def _print_name(self, b, cut_directory): 256 | if self.stereotype: 257 | with b.font(f"point-size=\"{Config.font_size_other!s}\""): 258 | b.htmltext(f"<<{self.stereotype}>>") 259 | b.text("
") 260 | split = self.label.rsplit("/", 1) 261 | name = split[-1] 262 | dir = split[0] + "/" if len(split) == 2 else "" 263 | if dir and not cut_directory: 264 | with b.font(f"point-size=\"{Config.font_size_other!s}\""): 265 | with b.html("sup"): 266 | b.htmltext(dir) 267 | b.text("
") 268 | with b.font(f"point-size=\"{Config.font_size_classname!s}\""): 269 | with b.b(): 270 | b.htmltext(name) 271 | 272 | def _print_attrs(self, b): 273 | tags = self._limit(self.attrs) 274 | self._print_tags(tags, Config.show_attr_type, b) 275 | 276 | def _print_ops(self, b): 277 | tags = self._limit(self.ops) 278 | self._print_tags(tags, Config.show_op_return_type, b, "()") 279 | 280 | def _limit(self, tags, limit=10): 281 | if len(tags) > limit: 282 | tags = tags.copy() 283 | # Cut off unchanged tags, starting from the end 284 | for i in reversed(range(len(tags))): 285 | if tags[i].diffsym == " ": 286 | tags.pop(i) 287 | if len(tags) == limit: 288 | break 289 | # Cut off remaining changed tags 290 | tags = tags[:limit] 291 | tags.append("...") 292 | return tags 293 | 294 | def _print_tags(self, tags, show_type_ref, b, suffix=""): 295 | num = 0 296 | for tag in tags: 297 | if tag == "...": 298 | with b.tr(): 299 | with b.td("border=\"0\" align=\"left\""): 300 | b.text("...") 301 | break 302 | text = tag.visibility if Config.show_visibility else "" 303 | text += tag.name 304 | text += suffix 305 | bgcolor = Gv.bgcolor(tag.diffsym) 306 | with b.tr(): 307 | with b.td(f"border=\"0\" align=\"left\"{bgcolor} " 308 | + Gv.search_href(tag.name)): 309 | if tag.is_static: 310 | with b.u(): 311 | b.htmltext(text) 312 | else: 313 | b.htmltext(text) 314 | if show_type_ref and tag.typeref != "-": 315 | with b.i(): 316 | b.htmltext(" : " + tag.typeref) 317 | num += 1 318 | 319 | 320 | class GvRel: 321 | def __init__(self, a_qn, a_use_file, b_qn, b_use_file, diffsym, type): 322 | if a_use_file: 323 | self._a_name = Gv.clustername(a_qn) 324 | else: 325 | self._a_name = Gv.nodename(a_qn) 326 | if b_use_file: 327 | self._b_name = Gv.clustername(b_qn) 328 | else: 329 | self._b_name = Gv.nodename(b_qn) 330 | self.diffsym = diffsym 331 | self.type = type 332 | 333 | def print(self, b): 334 | attr = Gv.fgcolor(self.diffsym) 335 | b.edge(self._a_name, self._b_name, attr) 336 | -------------------------------------------------------------------------------- /tests/Java.mockito.expected.gv: -------------------------------------------------------------------------------- 1 | digraph cl { 2 | bgcolor="white"; 3 | size="104.16666666666667,104.16666666666667"; 4 | dpi=96; 5 | outputorder=edgesfirst; 6 | maxiter=600; 7 | splines=compound; 8 | node [shape=none margin=0 style=filled fillcolor="#fff3d0" 9 | fontname="Roboto, Verdana, Arial" fontsize=11]; 10 | edge [arrowhead=open] 11 | subgraph cluster_p_src_main_java_org_mockito_internal_creation_bytebuddy { 12 | label=< 13 |
src/main/java/org/mockito/internal/creation/bytebuddy
>; 14 | bgcolor="white"; color="#777777"; penwidth=5;subgraph cluster_src_main_java_org_mockito_internal_creation_bytebuddy_SubclassBytecodeGenerator_java { 15 | style=dotted;penwidth=1;color=black;href="filename:src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java:gitk:scroll_to_file {src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java}" bgcolor="white"; label=< 16 |
SubclassBytecodeGenerator.java
>; 17 | style=invis; 18 | label=""; 19 | src_main_java_org_mockito_internal_creation_bytebuddy_SubclassBytecodeGenerator_java_SubclassBytecodeGenerator [label=< 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
SubclassBytecodeGenerator
-CODEGEN_PACKAGE
-MOCKITO_MODULE
-GET_MODULE
-IS_OPEN
-IS_EXPORTED
-CAN_READ
-ADD_EXPORTS
-ADD_READ
-ADD_OPENS
-GET_UNNAMED_MODULE
...
#SubclassBytecodeGenerator()
+mockClass()
-addReadEdgeByProbe()
-addExportEdgeByProbe()
-loadModuleProble()
-isGroovyMethod()
-nameFor()
-isComingFromJDK()
-isComingFromSealedPackage()
-assertModuleAccessability()
...
> ] 44 | } 45 | subgraph cluster_src_main_java_org_mockito_internal_creation_bytebuddy_SubclassInjectionLoader_java { 46 | style=dotted;penwidth=1;color=black;href="filename:src/main/java/org/mockito/internal/creation/bytebuddy/SubclassInjectionLoader.java:gitk:scroll_to_file {src/main/java/org/mockito/internal/creation/bytebuddy/SubclassInjectionLoader.java}" bgcolor="white"; label=< 47 |
SubclassInjectionLoader.java
>; 48 | style=invis; 49 | label=""; 50 | src_main_java_org_mockito_internal_creation_bytebuddy_SubclassInjectionLoader_java_SubclassInjectionLoader [label=< 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
SubclassInjectionLoader
-ERROR_MESSAGE
-loader
~SubclassInjectionLoader()
-tryLookup()
+isDisrespectingOpenness()
+resolveStrategy()
> ] 59 | } 60 | subgraph cluster_src_main_java_org_mockito_internal_creation_bytebuddy_SubclassLoader_java { 61 | style=dotted;penwidth=1;color=black;href="filename:src/main/java/org/mockito/internal/creation/bytebuddy/SubclassLoader.java:gitk:scroll_to_file {src/main/java/org/mockito/internal/creation/bytebuddy/SubclassLoader.java}" bgcolor="white"; label=< 62 |
SubclassLoader.java
>; 63 | style=invis; 64 | label=""; 65 | src_main_java_org_mockito_internal_creation_bytebuddy_SubclassLoader_java_SubclassLoader [label=< 66 | 67 | 68 | 69 |
<<interface>>
SubclassLoader
+isDisrespectingOpenness()
+resolveStrategy()
> ] 70 | } 71 | } 72 | subgraph p_subprojects_android_src_main_java_org_mockito_android_internal_creation { 73 | subgraph cluster_subprojects_android_src_main_java_org_mockito_android_internal_creation_AndroidLoadingStrategy_java { 74 | style=dotted;penwidth=1;color=black;href="filename:subprojects/android/src/main/java/org/mockito/android/internal/creation/AndroidLoadingStrategy.java:gitk:scroll_to_file {subprojects/android/src/main/java/org/mockito/android/internal/creation/AndroidLoadingStrategy.java}" bgcolor="white"; label=< 75 |
subprojects/android/src/main/java/org/mockito/android/internal/creation/AndroidLoadingStrategy.java
>; 76 | style=invis; 77 | label=""; 78 | subprojects_android_src_main_java_org_mockito_android_internal_creation_AndroidLoadingStrategy_java_AndroidLoadingStrategy [label=< 79 | 80 | 81 | 82 |
AndroidLoadingStrategy
+isDisrespectingOpenness()
+resolveStrategy()
> ] 83 | } 84 | } 85 | subgraph cluster_p_subprojects_module_test_src_test_java_org_mockito_moduletest { 86 | label=< 87 |
subprojects/module-test/src/test/java/org/mockito/moduletest
>; 88 | bgcolor="white"; color="#777777"; penwidth=5;subgraph cluster_subprojects_module_test_src_test_java_org_mockito_moduletest_CanLoadWithSimpleModule_java { 89 | style=dotted;penwidth=1;color=black;href="filename:subprojects/module-test/src/test/java/org/mockito/moduletest/CanLoadWithSimpleModule.java:gitk:scroll_to_file {subprojects/module-test/src/test/java/org/mockito/moduletest/CanLoadWithSimpleModule.java}" bgcolor="white"; label=< 90 |
CanLoadWithSimpleModule.java
>; 91 | style=invis; 92 | label=""; 93 | subprojects_module_test_src_test_java_org_mockito_moduletest_CanLoadWithSimpleModule_java_CanLoadWithSimpleModule [label=< 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
CanLoadWithSimpleModule
+can_define_class_in_open_reading_module()
+can_define_class_in_open_reading_private_module()
+can_define_class_in_open_non_reading_module()
+can_define_class_in_open_non_reading_non_exporting_module()
+can_define_class_in_closed_module()
+cannot_define_class_in_non_opened_non_exported_module()
-modularJar()
-type()
-moduleInfo()
-layer()
> fillcolor="#ffaaaa"] 106 | } 107 | subgraph cluster_subprojects_module_test_src_test_java_org_mockito_moduletest_ModuleHandlingTest_java { 108 | style=dotted;penwidth=1;color=black;href="filename:subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleHandlingTest.java:gitk:scroll_to_file {subprojects/module-test/src/test/java/org/mockito/moduletest/ModuleHandlingTest.java}" bgcolor="white"; label=< 109 |
ModuleHandlingTest.java
>; 110 | style=invis; 111 | label=""; 112 | subprojects_module_test_src_test_java_org_mockito_moduletest_ModuleHandlingTest_java_ModuleHandlingTest [label=< 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 |
ModuleHandlingTest
+can_define_class_in_open_reading_module()
+can_define_class_in_open_reading_private_module()
+can_define_class_in_open_non_reading_module()
+can_define_class_in_open_non_reading_non_exporting_module()
+can_define_class_in_closed_module()
+cannot_define_class_in_non_opened_non_exported_module()
-modularJar()
-type()
-moduleInfo()
-layer()
> fillcolor="#aaffaa"] 125 | } 126 | } 127 | src_main_java_org_mockito_internal_creation_bytebuddy_SubclassBytecodeGenerator_java_SubclassBytecodeGenerator -> src_main_java_org_mockito_internal_creation_bytebuddy_SubclassLoader_java_SubclassLoader [] 128 | src_main_java_org_mockito_internal_creation_bytebuddy_SubclassBytecodeGenerator_java_SubclassBytecodeGenerator -> src_main_java_org_mockito_internal_creation_bytebuddy_SubclassInjectionLoader_java_SubclassInjectionLoader [] 129 | edge [arrowhead=empty]; 130 | edge [style=dashed]; 131 | src_main_java_org_mockito_internal_creation_bytebuddy_SubclassInjectionLoader_java_SubclassInjectionLoader -> src_main_java_org_mockito_internal_creation_bytebuddy_SubclassLoader_java_SubclassLoader [] 132 | subprojects_android_src_main_java_org_mockito_android_internal_creation_AndroidLoadingStrategy_java_AndroidLoadingStrategy -> src_main_java_org_mockito_internal_creation_bytebuddy_SubclassLoader_java_SubclassLoader [] 133 | } 134 | 135 | -------------------------------------------------------------------------------- /classdiff/tag_model.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import os 3 | import sys 4 | from dataclasses import dataclass 5 | from enum import IntEnum 6 | from classdiff.tag_language import TagLanguage 7 | from classdiff.config import Config 8 | 9 | 10 | class Model(dict): 11 | _cached_file_names = None 12 | 13 | def find_scope(self, name, preferred_file): 14 | if self[preferred_file].has(name): 15 | return self[preferred_file].get_scope(name) 16 | for fname in self: 17 | if self[fname].has(name): 18 | return self[fname].get_scope(name) 19 | 20 | def get_file_by_scope(self, qualified_name): 21 | for fname in self: 22 | scope = self[fname].find_scope(qualified_name) 23 | if scope: 24 | return self[fname] 25 | 26 | def get_scope(self, qualified_name): 27 | for fname in self: 28 | scope = self[fname].find_scope(qualified_name) 29 | if scope: 30 | return scope 31 | 32 | def find_file(self, tag_ref, language): 33 | if tag_ref in self: 34 | return self[tag_ref] 35 | if not self._cached_file_names: 36 | self._cached_file_names = {f.split("/")[-1]: f 37 | for f in self.keys()} 38 | if tag_ref in self._cached_file_names: 39 | return self[self._cached_file_names[tag_ref]] 40 | 41 | def mark_changed_members(self): 42 | for fname in self: 43 | for scope in self[fname].get_scopes(): 44 | for member in scope.get_members(): 45 | if self[fname].has_diff_touching(member.range): 46 | if member.diffsym not in ("-", "+"): 47 | member.diffsym = "~" 48 | 49 | 50 | class File: 51 | def __init__(self, name=""): 52 | self.name = name 53 | self._scopes = {} 54 | self._diff_ranges = [] 55 | self.package = name.rsplit("/", 1)[0] if "/" in name else "" 56 | 57 | def add_scope(self, scope): 58 | self._scopes[scope.name] = scope 59 | 60 | def add_tag(self, tag, diffsym=""): 61 | scope = None 62 | if self.has(tag.scope): 63 | scope = self._scopes[tag.scope] 64 | elif tag.scope == "-" or tag.scope == "": 65 | scope = self.get_global_scope() 66 | elif (Config.show_nested_classes or 67 | tag.language.only_works_with_nesting()): 68 | local_name = tag.language.local_name(tag.scope) 69 | if self.has(local_name): 70 | scope = self._scopes[local_name] 71 | if scope: 72 | scope.add_tag(tag, diffsym) 73 | 74 | def has(self, scope): 75 | return scope in self._scopes 76 | 77 | def get_scope(self, key): 78 | return self._scopes[key] 79 | 80 | def find_scope(self, qualified_name): 81 | for scope in self.get_scopes(): 82 | if scope.qualified_name == qualified_name: 83 | return scope 84 | 85 | def get_scope_keys(self): 86 | return self._scopes.keys() 87 | 88 | def get_scopes(self): 89 | return self._scopes.values() 90 | 91 | def get_scope_count(self): 92 | return len(self._scopes) 93 | 94 | def get_global_scope(self): 95 | if not self.has(""): 96 | global_scope = Scope("", self.name) 97 | self.add_scope(global_scope) 98 | return self.get_scope("") 99 | 100 | def get_real_scopes(self): 101 | return [scope for scope in self.get_scopes() 102 | if not scope.is_global_scope()] 103 | 104 | def add_diff_range(self, r): 105 | self._diff_ranges.append(r) 106 | 107 | def has_diff_touching(self, other): 108 | for r in self._diff_ranges: 109 | if LineRange.touching(r, other): 110 | return True 111 | return False 112 | 113 | 114 | class Scope: 115 | def __init__(self, name="", namespace="", diffsym="", stereotype=""): 116 | self._tags = {} 117 | self.name = name 118 | self.qualified_name = self._get_qualified_name(name, namespace) 119 | self.diffsym = diffsym 120 | self.stereotype = stereotype 121 | self.tagval_inherits = [] 122 | self.rels = [] 123 | self.range = None 124 | 125 | def _get_qualified_name(self, name, namespace): 126 | return namespace + (f":{name}" if name else "") 127 | 128 | def add_tag(self, tag, diffsym=""): 129 | key = tag.name + diffsym 130 | self._tags[key] = tag 131 | tag.diffsym = diffsym 132 | if self.is_global_scope(): 133 | tag.is_static = False 134 | 135 | def is_interface(self): 136 | return self.stereotype == "interface" 137 | 138 | def add_inherit(self, other): 139 | self.tagval_inherits.append(other) 140 | 141 | def add_rel(self, other_qn, type, diffsym=""): 142 | if self.get_rel(other_qn): 143 | # Only allow one rel 144 | return 145 | self.rels.append(Rel(other_qn, type, diffsym)) 146 | 147 | def get_rel_other_qns(self): 148 | return [rel.other_qn for rel in self.rels] 149 | 150 | def get_rel(self, other_qn): 151 | for rel in self.rels: 152 | if rel.other_qn == other_qn: 153 | return rel 154 | 155 | def get_tag(self, key): 156 | return self._tags[key] 157 | 158 | def get_tags_dict(self): 159 | return self._tags 160 | 161 | def get_attrs(self): 162 | return [t for t in self._tags.values() if isinstance(t, Attr)] 163 | 164 | def get_ops(self): 165 | return [t for t in self._tags.values() if isinstance(t, Op)] 166 | 167 | def get_invisible_tags(self): 168 | return [t for t in self._tags.values() if isinstance(t, InvisibleTag)] 169 | 170 | def get_tag_keys(self): 171 | return self._tags.keys() 172 | 173 | def get_members(self): 174 | return self.get_attrs() + self.get_ops() 175 | 176 | def is_empty(self): 177 | return not self.get_members() 178 | 179 | def is_global_scope(self): 180 | return self.name == "" 181 | 182 | def touches(self, range): 183 | if self.range and LineRange.touching(range, self.range): 184 | return True 185 | for tag in self.get_members(): 186 | if LineRange.touching(range, tag.range): 187 | return True 188 | return False 189 | 190 | 191 | class RelType(IntEnum): 192 | DERIV = 1 193 | REAL = 2 194 | ASSOC = 3 195 | 196 | 197 | class Rel: 198 | def __init__(self, other_qn, type, diffsym=""): 199 | self.other_qn = other_qn 200 | self.type = type 201 | self.diffsym = diffsym 202 | 203 | 204 | @dataclass 205 | class Tag(): 206 | fname: str 207 | kind: str 208 | roles: str 209 | name: str 210 | typeref: str 211 | scope: str 212 | visibility: str 213 | diffsym: str 214 | range 215 | 216 | def __init__(self): 217 | pass 218 | 219 | 220 | @dataclass 221 | class Attr(Tag): 222 | def __init__(self): 223 | super().__init__() 224 | 225 | 226 | @dataclass 227 | class Op(Tag): 228 | def __init__(self): 229 | super().__init__() 230 | 231 | 232 | @dataclass 233 | class InvisibleTag(Tag): 234 | def __init__(self): 235 | super().__init__() 236 | 237 | 238 | class ModelFactory: 239 | def fromfile(self, tagpath): 240 | model = Model() 241 | 242 | if os.path.isdir(tagpath): 243 | tagpath = os.path.join(tagpath, "tags") 244 | if not os.path.isfile(tagpath): 245 | print("error:file not found: " + tagpath, file=sys.stderr) 246 | exit(1) 247 | with open(tagpath) as tagfile: 248 | for line in tagfile: 249 | if line.startswith("!_"): 250 | continue 251 | cols = line.rstrip("\n").split("\t") 252 | fname = cols[0] 253 | roles = cols[1] 254 | kind = cols[2] 255 | access = cols[6] 256 | implementation = cols[7] 257 | language = TagLanguage(cols[9]) 258 | name = language.name(cols[3]) 259 | scope = language.scope(cols[5]) 260 | if language.is_blacklisted(): 261 | continue 262 | if fname not in model: 263 | model[fname] = File(fname) 264 | if language.is_scope(kind, roles, access, name): 265 | stereotype = language.get_scope_stereotype(kind, 266 | implementation) 267 | t = Scope(name, fname, stereotype=stereotype) 268 | inherits = cols[8] 269 | if inherits != "" and inherits != "-": 270 | for i in language.split_inherits(inherits): 271 | t.add_inherit(i) 272 | if not Config.show_nested_classes \ 273 | and language.is_nested(scope) \ 274 | and not language.only_works_with_nesting(): 275 | continue 276 | elif language.is_attr(kind, roles, access, name): 277 | t = Attr() 278 | elif language.is_op(kind, roles, access, name): 279 | t = Op() 280 | elif language.is_inv_tag(kind, roles, access, name): 281 | t = InvisibleTag() 282 | else: 283 | continue 284 | t.fname = fname 285 | t.roles = roles 286 | t.kind = kind 287 | t.name = name 288 | t.typeref = language.typeref(cols[4]) 289 | t.scope = scope 290 | t.visibility = language.visibility(access) 291 | t.language = language 292 | t.is_static = language.is_static_member(kind, implementation) 293 | start_line = int(cols[10]) if cols[10].isdigit() else None 294 | end_line = int(cols[11]) if cols[11].isdigit() else None 295 | t.range = LineRange(start_line, end_line) 296 | 297 | if isinstance(t, Scope): 298 | model[fname].add_scope(t) 299 | else: 300 | model[fname].add_tag(t) 301 | 302 | self.complete_relationships_inheritance(model) 303 | self.complete_relationships_assoc(model) 304 | return model 305 | 306 | def complete_relationships_inheritance(self, model): 307 | for fname in model: 308 | for scope in model[fname].get_scopes(): 309 | for inherit in scope.tagval_inherits: 310 | other_scope = model.find_scope(inherit, fname) 311 | if not other_scope: 312 | # Ignore scopes we don't know 313 | continue 314 | if other_scope.is_interface(): 315 | reltype = RelType.REAL 316 | else: 317 | reltype = RelType.DERIV 318 | scope.add_rel(other_scope.qualified_name, reltype) 319 | 320 | def complete_relationships_assoc(self, model): 321 | for fname in model: 322 | file = model[fname] 323 | for scope in file.get_scopes(): 324 | for key in scope.get_tags_dict(): 325 | tag = scope.get_tag(key) 326 | if tag.roles == "def": 327 | continue 328 | found_file = model.find_file(tag.name, tag.language) 329 | if found_file: 330 | other_scope = found_file.get_global_scope() 331 | else: 332 | # Also look for references to scopes 333 | local_name = tag.language.local_name(tag.name) 334 | other_scope = model.find_scope(local_name, fname) 335 | if other_scope: 336 | other_file = model.get_file_by_scope( 337 | other_scope.qualified_name) 338 | start_scope = self.pick_rel_scope(file, scope, 339 | tag.range) 340 | other_scope = self.pick_rel_scope(other_file, 341 | other_scope) 342 | if other_scope == start_scope: 343 | # Ignore self-reference 344 | continue 345 | if (start_scope.is_global_scope() and 346 | file.find_scope(other_scope.qualified_name)): 347 | # Ignore reference from a file to its own scopes 348 | continue 349 | start_scope.add_rel(other_scope.qualified_name, 350 | RelType.ASSOC) 351 | 352 | def pick_rel_scope(self, file, scope, line_range=None): 353 | if (scope.is_global_scope() and scope.is_empty() 354 | and file.get_scope_count() == 2): 355 | return file.get_real_scopes()[0] 356 | if (Config.find_rel_scope_using_line_numbers 357 | and scope.is_global_scope() 358 | and line_range): 359 | for s in file.get_scopes(): 360 | if s.touches(line_range): 361 | return s 362 | return scope 363 | 364 | def diff(self, model_a, model_b): 365 | model = Model() 366 | fnames = self._merge_in_order(list(model_a.keys()), 367 | list(model_b.keys())) 368 | for fname in fnames: 369 | model[fname] = File(fname) 370 | file_a = model_a[fname] if fname in model_a else File() 371 | file_b = model_b[fname] if fname in model_b else File() 372 | snames = self._merge_in_order(list(file_a.get_scope_keys()), 373 | list(file_b.get_scope_keys())) 374 | for sname in snames: 375 | scope_a = (file_a.get_scope(sname) if file_a.has(sname) else 376 | Scope()) 377 | scope_b = (file_b.get_scope(sname) if file_b.has(sname) else 378 | Scope()) 379 | tnames = self._merge_in_order(list(scope_a.get_tag_keys()), 380 | list(scope_b.get_tag_keys())) 381 | sdiffsym = (" " if file_a.has(sname) and file_b.has(sname) else 382 | "-" if file_a.has(sname) else 383 | "+") 384 | stereotype = scope_b.stereotype or scope_a.stereotype 385 | scope = Scope(sname, fname, sdiffsym, stereotype) 386 | other_qns = self._merge_in_order( 387 | list(scope_a.get_rel_other_qns()), 388 | list(scope_b.get_rel_other_qns())) 389 | for other_qn in other_qns: 390 | rel_a = scope_a.get_rel(other_qn) 391 | rel_b = scope_b.get_rel(other_qn) 392 | rdiffsym = (" " if (rel_a and rel_b) else 393 | "-" if rel_a else 394 | "+") 395 | reltype = rel_b and rel_b.type or rel_a.type 396 | scope.add_rel(other_qn, reltype, rdiffsym) 397 | 398 | model[fname].add_scope(scope) 399 | for tname in tnames: 400 | if (tname in scope_a.get_tag_keys() 401 | and tname in scope_b.get_tag_keys() 402 | and (scope_a.get_tag(tname) == 403 | scope_b.get_tag(tname))): 404 | scope.add_tag(copy.copy(scope_b.get_tag(tname)), " ") 405 | else: 406 | if tname in scope_a.get_tag_keys(): 407 | scope.add_tag(copy.copy(scope_a.get_tag(tname)), 408 | "-") 409 | if tname in scope_b.get_tag_keys(): 410 | scope.add_tag(copy.copy(scope_b.get_tag(tname)), 411 | "+") 412 | return model 413 | 414 | def _merge_in_order(self, a, b): 415 | a2 = list(a) 416 | b2 = list(b) 417 | result = list() 418 | while len(a2) > 0: 419 | v1 = a2[0] 420 | if v1 in b2: 421 | while b2[0] != v1: 422 | v2 = b2[0] 423 | if v2 in a2: 424 | a2.remove(v2) 425 | b2.remove(v2) 426 | result.append(v2) 427 | a2.remove(v1) 428 | if v1 in b2: 429 | b2.remove(v1) 430 | result.append(v1) 431 | return result + b2 432 | 433 | 434 | class LineRange: 435 | def __init__(self, start, end): 436 | if start and not end: 437 | end = start 438 | self.start = start 439 | self.end = end 440 | self.valid = start and end 441 | 442 | def touching(a, b): 443 | return a and b and \ 444 | a.valid and b.valid and \ 445 | a.end >= b.start and b.end >= a.start 446 | -------------------------------------------------------------------------------- /gitk-cl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env tclsh 2 | set gitkpath [exec which gitk] 3 | if {![file exists $gitkpath]} { 4 | catch { 5 | set gitkpath [exec cygpath --windows $gitkpath] 6 | } 7 | } 8 | source $gitkpath 9 | 10 | set lastms [clock clicks -milliseconds] 11 | proc log {text {end \n}} { 12 | global lastms 13 | global debugtext 14 | 15 | set curms [clock clicks -milliseconds] 16 | set diff [expr { $curms - $lastms }] 17 | if {$end != ""} { 18 | set end " ($diff ms) $end" 19 | } 20 | append debugtext $text $end 21 | update 22 | 23 | set lastms $curms 24 | } 25 | 26 | # addtocflist: Replace gitk's original 27 | rename addtocflist orig_addtocflist 28 | proc addtocflist {ids} { 29 | create_ui 30 | orig_addtocflist $ids 31 | update 32 | make_diagram 33 | } 34 | 35 | proc make_diagram {} { 36 | global latest_imgpath latest_dirs 37 | global showdiagram 38 | 39 | if {!$showdiagram} { 40 | return 41 | } 42 | nowbusy gitk-cl 43 | set dirs [put_src_files] 44 | set dir1 [lindex $dirs 0] 45 | set dir2 [lindex $dirs 1] 46 | set dir3 [lindex $dirs 2] 47 | set imgpath $dir3/classes 48 | set rangespath $dir3/ranges 49 | set latest_dirs $dirs 50 | set latest_imgpath $imgpath 51 | 52 | global async_callback_queue 53 | set async_callback_queue {} 54 | lappend async_callback_queue [concat "generate_ctags" $dir1] 55 | lappend async_callback_queue [concat "generate_ctags" $dir2] 56 | lappend async_callback_queue [concat "generate_gv" $dir1 $dir2 $imgpath $rangespath] 57 | lappend async_callback_queue [concat "generate_img" $dir1 $dir2 $imgpath] 58 | lappend async_callback_queue [concat "load_img" $imgpath] 59 | 60 | generate_diff_ranges $rangespath 61 | } 62 | 63 | # clear_ctext: Replace gitk's original 64 | rename clear_ctext orig_clear_ctext 65 | proc clear_ctext {{first 1.0}} { 66 | orig_clear_ctext $first 67 | clear_previous_image 68 | clear_canvas 69 | clear_debugtext 70 | reset_zoomlevel 71 | } 72 | 73 | proc create_ui {} { 74 | global NS ctxbut 75 | global brightdiagram zoomlevel 76 | global showflist showdiagram 77 | 78 | if {[info exists brightdiagram]} { 79 | return 80 | } 81 | 82 | set brightdiagram [${NS}::frame .bright.diagram] 83 | set canvas .bright.diagram.canvas 84 | canvas $canvas 85 | pack $canvas -anchor nw 86 | pack forget .bright.cfiles 87 | if {$showdiagram} { 88 | pack .bright.diagram -side right -fill both -expand 1 89 | } 90 | if {$showflist} { 91 | pack .bright.cfiles -side left -fill both -expand 1 92 | } 93 | 94 | set diagram_menu .diagramctxmenu 95 | makemenu $diagram_menu { 96 | {mc "Open PNG" command {open_png}} 97 | {mc "Copy path: old code directory" command {copy_path_dir 0}} 98 | {mc "Copy path: new code directory" command {copy_path_dir 1}} 99 | {mc "Copy path: output directory" command {copy_path_dir 2}} 100 | {mc "Show debug info" command {show_debug_info}} 101 | } 102 | $diagram_menu configure -tearoff 0 103 | 104 | bind .bright.diagram $ctxbut {pop_diagram_menu %W %X %Y %x %y} 105 | bind .bright.diagram.canvas $ctxbut {pop_diagram_menu %W %X %Y %x %y} 106 | bind .bright.diagram.canvas {%W scan mark %x %y 107 | set dragged 0 108 | set xdrag [expr {![scaled_img_fully_visible_x]}] 109 | set ydrag [expr {![scaled_img_fully_visible_y]}] 110 | } 111 | bind .bright.diagram.canvas { 112 | if {$dragged <= 10} { 113 | mouse_left %x %y 114 | } 115 | } 116 | bind .bright.diagram.canvas { 117 | %W scan dragto [expr %x * $xdrag] [expr %y * $ydrag] 1 118 | incr dragged 119 | } 120 | bind .bright.diagram.canvas {zoom $orig_image $scaled_image -1 %x %y} 121 | bind .bright.diagram.canvas {zoom $orig_image $scaled_image 1 %x %y} 122 | bind .bright.diagram.canvas {zoom $orig_image $scaled_image %D %x %y} 123 | 124 | ${NS}::checkbutton .bright.mode.checkflist -text [mc "Files"] \ 125 | -command mod_ui -variable showflist 126 | ${NS}::checkbutton .bright.mode.checkdiagram -text [mc "Diagram"] \ 127 | -command mod_ui -variable showdiagram 128 | grid forget .bright.mode.patch .bright.mode.tree 129 | grid .bright.mode.patch .bright.mode.tree .bright.mode.checkflist .bright.mode.checkdiagram - -sticky ew -padx 4 130 | } 131 | 132 | proc register_config {var default_val} { 133 | global config_variables 134 | global $var 135 | if {![info exists $var]} { 136 | set $var $default_val 137 | } 138 | lappend config_variables $var 139 | config_init_trace $var 140 | trace add variable $var write config_variable_change_cb 141 | } 142 | 143 | register_config showdiagram 1 144 | register_config showflist 1 145 | proc mod_ui {} { 146 | global showflist showdiagram 147 | 148 | pack forget .bright.cfiles 149 | pack forget .bright.diagram 150 | if {$showdiagram} { 151 | pack .bright.diagram -side right -fill both -expand 1 152 | } 153 | if {$showflist} { 154 | pack .bright.cfiles -side left -fill both -expand 1 155 | } 156 | make_diagram 157 | } 158 | 159 | proc mouse_left {x y} { 160 | global imap zoomlevel 161 | 162 | set unzoomedx [expr {[.bright.diagram.canvas canvasx $x] * $zoomlevel}] 163 | set unzoomedy [expr {[.bright.diagram.canvas canvasy $y] * $zoomlevel}] 164 | 165 | set evalables_sorted {} 166 | dict for {id val} $imap { 167 | dict with val { 168 | if {$unzoomedx >= $left && $unzoomedx <= $right && 169 | $unzoomedy >= $top && $unzoomedy <= $bottom} { 170 | if {[string match *scroll_to_file* $evalable]} { 171 | # do scroll before search 172 | set evalables_sorted [linsert $evalables_sorted 0 $evalable] 173 | } else { 174 | lappend evalables_sorted $evalable 175 | } 176 | } 177 | } 178 | } 179 | foreach evalable $evalables_sorted { 180 | {*}$evalable 181 | } 182 | } 183 | 184 | proc pop_diagram_menu {w X Y x y} { 185 | tk_popup .diagramctxmenu $X $Y 186 | } 187 | 188 | proc clear_previous_image {} { 189 | global imap 190 | 191 | if {[info exists imap]} { 192 | diagram_img blank 193 | scaled_img blank 194 | } 195 | } 196 | 197 | proc clear_canvas {} { 198 | global imap 199 | 200 | if {[info exists imap]} { 201 | .bright.diagram.canvas delete all 202 | } 203 | } 204 | 205 | proc clear_debugtext {} { 206 | global debugtext 207 | 208 | set debugtext "" 209 | } 210 | 211 | proc reset_zoomlevel {} { 212 | global zoomlevel 213 | 214 | set zoomlevel 1 215 | } 216 | 217 | proc put_src_files {} { 218 | global nullid nullid2 219 | global flist_menu_file diffids 220 | global parentlist selectedline 221 | 222 | # See gitk's external_diff 223 | if {[llength $diffids] == 1} { 224 | set diffidto [lindex $diffids 0] 225 | if {$diffidto eq $nullid} { 226 | set diffidfrom $nullid2 227 | } elseif {$diffidto eq $nullid2} { 228 | set diffidfrom "HEAD" 229 | } else { 230 | set diffidfrom [lindex $parentlist $selectedline 0] 231 | } 232 | } else { 233 | set diffidfrom [lindex $diffids 0] 234 | set diffidto [lindex $diffids 1] 235 | } 236 | 237 | set difffromdir [gitknewtmpdir] 238 | set difftodir [gitknewtmpdir] 239 | set imgdir [gitknewtmpdir] 240 | 241 | put_files $diffidfrom $difffromdir 242 | put_files $diffidto $difftodir 243 | 244 | return [list $difffromdir $difftodir $imgdir] 245 | } 246 | 247 | proc put_files {diffid dir} { 248 | global worktree treediffs diffids 249 | global nullid nullid2 250 | 251 | if {$diffid eq {}} { 252 | return 253 | } 254 | if {$diffid eq $nullid} { 255 | # Copy from the working directory 256 | foreach filename $treediffs($diffids) { 257 | set destfile [file join $dir $filename] 258 | set sourcefile $worktree/$filename 259 | if {[file exists $sourcefile]} { 260 | if {![file exists [file dirname $destfile]]} { 261 | file mkdir [file dirname $destfile] 262 | } 263 | file copy -- $sourcefile $destfile 264 | } 265 | } 266 | return 267 | } 268 | set files_esc [shell_escape $treediffs($diffids)] 269 | if {$diffid eq $nullid2} { 270 | # Copy from the index 271 | set cmd "git -C $worktree checkout-index -q --prefix=$dir/ -- $files_esc" 272 | exec_sync $cmd 273 | return 274 | } 275 | 276 | # Ignore files that don't exist in this version, otherwise git archive will fail 277 | set cmd "git -C $worktree ls-tree --name-only --full-tree -zr $diffid -- $files_esc" 278 | set files [exec_sync $cmd] 279 | set files [string trimright $files \0] 280 | if {$files ne {}} { 281 | # Copy from commit 282 | set files_esc [shell_escape [split $files "\0"]] 283 | set cmd "git -C $worktree archive $diffid $files_esc | tar xC $dir" 284 | exec_sync $cmd 285 | } 286 | } 287 | 288 | proc shell_escape {lst} { 289 | set escaped {} 290 | foreach s $lst { 291 | if {[string first ' $s] != -1} { 292 | # Ignore files with single quotes since our escaping can't handle them 293 | continue 294 | } 295 | append escaped "'$s' " 296 | } 297 | return $escaped 298 | } 299 | 300 | proc generate_diff_ranges {outputpath} { 301 | global ignorespace 302 | global limitdiffs vfilelimit curview 303 | global diffids 304 | 305 | set diffcmd [diffcmd $diffids "-p --submodule --no-commit-id -U0 --no-prefix"] 306 | if {$ignorespace} { 307 | append diffcmd " -w" 308 | } 309 | if {$limitdiffs && $vfilelimit($curview) ne {}} { 310 | set diffcmd [concat $diffcmd -- $vfilelimit($curview)] 311 | } 312 | 313 | set diffcmd [string trimleft $diffcmd "| "] 314 | set cmd [list | sh -c "$diffcmd | grep '^+++\\|^@@' | tee $outputpath"] 315 | exec_async $cmd 316 | } 317 | 318 | proc generate_ctags {srcdir} { 319 | # Use sh -c to prevent tab conversion 320 | set cmd [list | sh -c "ctags --sort=no --extras=+r -Rxf $srcdir/tags --tag-relative=always --_xformat='%{input} %{roles} %{kind} %{name} %{typeref} %{scope} %{access} %{implementation} %{inherits} %{language} %{line} %{end}' $srcdir"] 321 | exec_async $cmd 322 | } 323 | 324 | proc generate_gv {tags1 tags2 imgpath rangespath} { 325 | set cmd [list | classdiff $tags1 $tags2 -o $imgpath.gv -r $rangespath] 326 | foreach arg [get_config_args] { 327 | lappend cmd -e 328 | lappend cmd $arg 329 | } 330 | exec_async $cmd 331 | } 332 | 333 | proc generate_img {tags1 tags2 imgpath} { 334 | set cmd [list | fdp -Tpng -o $imgpath.png -Timap_np -o $imgpath.imap_np $imgpath.gv] 335 | exec_async $cmd 336 | } 337 | 338 | proc load_img {imgpath} { 339 | global imap scaled_image orig_image showflist 340 | 341 | set orig_image [image create photo diagram_img -file "$imgpath.png"] 342 | set scaled_image [image create photo scaled_img] 343 | scaled_img copy $orig_image 344 | set factor 0.85 345 | if {!$showflist} { 346 | set factor 1.0 347 | } 348 | set max_width [expr { 349 | round(([winfo width .bright.diagram] 350 | + [winfo width .bright.cfiles]) * $factor) 351 | }] 352 | set max_height [winfo height .bright.diagram] 353 | set img_width [image width $scaled_image] 354 | set img_height [image height $scaled_image] 355 | set canvas_width [expr min($img_width, $max_width)] 356 | set canvas_height [expr min($img_height, $max_height)] 357 | .bright.diagram.canvas create image 0 0 -image $scaled_image -anchor nw 358 | .bright.diagram.canvas configure -width $canvas_width -height $canvas_height 359 | .bright.diagram.canvas configure -scrollregion [.bright.diagram.canvas bbox all] 360 | .bright.diagram.canvas xview moveto 0 361 | .bright.diagram.canvas yview moveto 0 362 | 363 | set imap [dict create] 364 | set file [open "$imgpath.imap_np"] 365 | while {[gets $file line] > -1} { 366 | if {[regexp "^rect (?:filename:(.*):)?gitk:(.*) (\\d+),(\\d+) (\\d+),(\\d+)$" $line match fname evalable left top right bottom]} { 367 | set key $match 368 | if {$fname ne {}} { 369 | set key $fname 370 | dict set imap $key fname $fname 371 | } 372 | dict set imap $key left $left 373 | dict set imap $key top $top 374 | dict set imap $key right $right 375 | dict set imap $key bottom $bottom 376 | dict set imap $key evalable $evalable 377 | } 378 | } 379 | close $file 380 | notbusy gitk-cl 381 | } 382 | 383 | proc exec_sync {cmd} { 384 | log "$cmd..." "" 385 | if {[catch { 386 | set val [exec sh -c $cmd] 387 | } err]} { 388 | log "error\n $err" 389 | return -code error $err 390 | } 391 | log "done" 392 | return $val 393 | } 394 | 395 | proc exec_async {cmd} { 396 | global latest_fd 397 | 398 | log "$cmd..." "" 399 | set latest_fd [open $cmd r] 400 | fconfigure $latest_fd -blocking 0 401 | filerun $latest_fd [list poll_async $latest_fd] 402 | } 403 | 404 | proc poll_async {fd} { 405 | global latest_fd 406 | global async_callback_queue 407 | 408 | while {[gets $fd line] >= 0} { 409 | # Ignore stdout. Diff ranges are printed there, but we get them with tee. 410 | } 411 | if {[eof $fd]} { 412 | fconfigure $fd -blocking 1 413 | if {[catch {close $fd} err options]} { 414 | set status 0 415 | if {[lindex [dict get $options -errorcode] 0] eq "CHILDSTATUS"} { 416 | set status [lindex [dict get $options -errorcode] 2] 417 | append err " (exit code $status)" 418 | } 419 | notbusy gitk-cl 420 | log "error\n $err" 421 | error_popup $err 422 | if {$status != 0} { 423 | return 0 424 | } 425 | } else { 426 | log "done" 427 | } 428 | if {$latest_fd eq $fd} { 429 | set callback [queue_next async_callback_queue] 430 | if {$callback ne {}} { 431 | {*}$callback 432 | } 433 | } 434 | return 0 435 | } 436 | return 1 437 | } 438 | 439 | proc queue_next {name} { 440 | # https://wiki.tcl-lang.org/page/Stacks+and+queues 441 | upvar 1 $name queue 442 | set res [lindex $queue 0] 443 | set queue [lreplace $queue [set queue 0] 0] 444 | set res 445 | } 446 | 447 | proc open_png {} { 448 | global latest_imgpath 449 | 450 | launch_file_viewer [file normalize $latest_imgpath.png] 451 | } 452 | 453 | proc copy_path_dir {index} { 454 | global latest_dirs 455 | 456 | clipboard clear 457 | clipboard append [file normalize [lindex $latest_dirs $index]] 458 | } 459 | 460 | proc launch_file_viewer {path} { 461 | # https://wiki.tcl-lang.org/page/Invoking+browsers 462 | if {[catch { 463 | set programs {xdg-open open start firefox} 464 | foreach program $programs { 465 | if {$program eq "start"} { 466 | set cmd [list {*}[auto_execok start] {}] 467 | } else { 468 | set cmd [auto_execok $program] 469 | } 470 | if {[string length $cmd]} { 471 | break 472 | } 473 | } 474 | exec {*}$cmd $path & 475 | } err]} { 476 | error_popup $err 477 | } 478 | } 479 | 480 | proc show_debug_info {} { 481 | global debugtext 482 | 483 | if {[confirm_popup "$debugtext\n\nCopy to clipboard?"]} { 484 | clipboard clear 485 | clipboard append $debugtext 486 | } 487 | } 488 | 489 | proc get_config_args {} { 490 | global clconfig 491 | set r {} 492 | foreach arg [split $clconfig "\n"] { 493 | lappend r $arg 494 | } 495 | return $r 496 | } 497 | 498 | proc search_next {search_term} { 499 | # Called by evalable 500 | global searchstring 501 | global latest_scroll_file 502 | 503 | if {$searchstring eq $search_term} { 504 | dosearch 505 | } else { 506 | scroll_to_file $latest_scroll_file 1 507 | reset_search_markers 508 | set searchstring $search_term 509 | } 510 | } 511 | 512 | proc scroll_to_file {name {force 0}} { 513 | # Called by evalable 514 | global ctext_file_names difffilestart ctext 515 | global latest_scroll_file 516 | 517 | if {[info exists latest_scroll_file] && $latest_scroll_file eq $name && !$force} { 518 | # Scrolling to the same position twice is usually unnecessary 519 | # It's better to allow a search to scroll to other places 520 | return 521 | } 522 | 523 | set i [lsearch -exact $ctext_file_names $name] 524 | if {$i >= 0} { 525 | set loc [lindex $difffilestart $i] 526 | $ctext yview $loc 527 | } 528 | set latest_scroll_file $name 529 | } 530 | 531 | proc reset_search_markers {} { 532 | global ctext 533 | 534 | # Resetting the search markers will start the next search from the 535 | # scroll position rather than from previous search results 536 | $ctext tag remove sel 1.0 end 537 | $ctext mark unset anchor 538 | } 539 | 540 | # highlightfile: Replace gitk's original 541 | rename highlightfile orig_highlightfile 542 | proc highlightfile {cline} { 543 | orig_highlightfile $cline 544 | draw_file_rectangle 1 545 | } 546 | 547 | # sel_flist: Replace gitk's original 548 | rename sel_flist orig_sel_flist 549 | proc sel_flist {w x y} { 550 | orig_sel_flist $w $x $y 551 | draw_file_rectangle 1 552 | } 553 | 554 | proc draw_file_rectangle {focus} { 555 | global ctext_file_names cflist_top imap 556 | global zoomlevel 557 | 558 | if {![info exists imap] || ![info exists cflist_top]} { 559 | return 560 | } 561 | 562 | .bright.diagram.canvas delete rect 563 | set i [expr {$cflist_top - 2}] 564 | if {$i >= 0} { 565 | set f [lindex $ctext_file_names $i] 566 | if {[dict exists $imap $f]} { 567 | set left [expr {[dict get $imap $f left] / $zoomlevel}] 568 | set top [expr {[dict get $imap $f top] / $zoomlevel}] 569 | set right [expr {[dict get $imap $f right] / $zoomlevel}] 570 | set bottom [expr {[dict get $imap $f bottom] / $zoomlevel}] 571 | set rect [ \ 572 | .bright.diagram.canvas create rectangle $left $top \ 573 | $right $bottom -outline blue -width 3 574 | ] 575 | .bright.diagram.canvas addtag rect withtag $rect 576 | if {$focus} { 577 | canvas_focus_items .bright.diagram.canvas $rect 578 | } 579 | } 580 | } 581 | } 582 | 583 | proc canvas_focus_items {c items} { 584 | # https://wiki.tcl-lang.org/page/canvas 585 | set box [eval $c bbox $items] 586 | 587 | if {$box == ""} { return } 588 | 589 | foreach { x y x1 y1 } $box break 590 | foreach { top btm } [$c yview] break 591 | foreach { left right } [$c xview] break 592 | foreach { p q xmax ymax } [$c cget -scrollregion] break 593 | 594 | set xpos [expr (($x1+$x) / 2.0) / $xmax - ($right-$left) / 2.0] 595 | set ypos [expr (($y1+$y) / 2.0) / $ymax - ($btm-$top) / 2.0] 596 | 597 | if {[scaled_img_fully_visible_x]} { 598 | set xpos 0 599 | } 600 | if {[scaled_img_fully_visible_y]} { 601 | set ypos 0 602 | } 603 | $c xview moveto $xpos 604 | $c yview moveto $ypos 605 | } 606 | 607 | proc scaled_img_fully_visible_x {} { 608 | global scaled_image 609 | 610 | set img_width [image width $scaled_image] 611 | set max_width [winfo width .bright.diagram] 612 | return [expr {$img_width < $max_width}] 613 | } 614 | 615 | proc scaled_img_fully_visible_y {} { 616 | global scaled_image 617 | 618 | set img_height [image height $scaled_image] 619 | set max_height [winfo height .bright.diagram] 620 | return [expr {$img_height < $max_height}] 621 | } 622 | 623 | proc zoom {srcimg destimg direction x y} { 624 | global zoomlevel 625 | 626 | set unzoomedx [expr {[.bright.diagram.canvas canvasx $x] * $zoomlevel}] 627 | set unzoomedy [expr {[.bright.diagram.canvas canvasy $y] * $zoomlevel}] 628 | 629 | if {$direction < 0} { 630 | incr zoomlevel -1 631 | if {$zoomlevel < 1} { 632 | set zoomlevel 1 633 | } 634 | } else { 635 | if {![scaled_img_fully_visible_x] || ![scaled_img_fully_visible_y]} { 636 | incr zoomlevel 637 | } 638 | } 639 | 640 | $destimg blank 641 | $destimg copy $srcimg -shrink -subsample $zoomlevel 642 | 643 | set img_width [image width $destimg] 644 | set img_height [image height $destimg] 645 | .bright.diagram.canvas configure -scrollregion [list 0 0 $img_width $img_height] 646 | foreach { p q xmax ymax } [.bright.diagram.canvas cget -scrollregion] break 647 | set zoomedx [expr {($unzoomedx / $zoomlevel - $x) / $xmax}] 648 | set zoomedy [expr {($unzoomedy / $zoomlevel - $y) / $ymax}] 649 | if {$zoomedx < 0} { 650 | set zoomedx 0 651 | } 652 | if {$zoomedy < 0} { 653 | set zoomedy 0 654 | } 655 | .bright.diagram.canvas xview moveto $zoomedx 656 | .bright.diagram.canvas yview moveto $zoomedy 657 | draw_file_rectangle 0 658 | } 659 | 660 | register_config clconfig "# Copy and change settings from config.py\n# Example:\n#Config.cluster_packages = False" 661 | # prefspage_general : Replace gitk's original 662 | rename prefspage_general orig_prefspage_general 663 | proc prefspage_general {notebook} { 664 | global NS bgcolor fgcolor 665 | global clconfig 666 | 667 | set page [orig_prefspage_general $notebook] 668 | text $page.gitkclt -background $bgcolor -foreground $fgcolor \ 669 | -font textfont 670 | $page.gitkclt insert end $clconfig 671 | bind $page.gitkclt "copy_config_text_to_var $page" 672 | ${NS}::frame $page.gitkclf 673 | ${NS}::label $page.gitkcll -text [mc "Class diagram"] 674 | grid $page.gitkcll - -sticky nw -pady 10 675 | grid x $page.gitkclf $page.gitkclt -sticky ew 676 | 677 | return $page 678 | } 679 | 680 | proc copy_config_text_to_var {page} { 681 | global clconfig 682 | set clconfig [$page.gitkclt get 1.0 {end -1c}] 683 | } 684 | 685 | # vim: ft=tcl ts=8 sts=4 sw=4 686 | -------------------------------------------------------------------------------- /tests/Python.flake8.expected.gv: -------------------------------------------------------------------------------- 1 | digraph cl { 2 | bgcolor="white"; 3 | size="104.16666666666667,104.16666666666667"; 4 | dpi=96; 5 | outputorder=edgesfirst; 6 | maxiter=600; 7 | splines=compound; 8 | node [shape=none margin=0 style=filled fillcolor="#fff3d0" 9 | fontname="Roboto, Verdana, Arial" fontsize=11]; 10 | edge [arrowhead=open] 11 | subgraph cluster_p_docs_source_release_notes { 12 | label=< 13 |
docs/source/release-notes
>; 14 | bgcolor="white"; color="#777777"; penwidth=5;subgraph cluster_docs_source_release_notes_______rst { 15 | style=dotted;penwidth=1;color=black;href="filename:docs/source/release-notes/3.7.2.rst:gitk:scroll_to_file {docs/source/release-notes/3.7.2.rst}" bgcolor="white"; label=< 16 |
3.7.2.rst
>; 17 | style=invis; 18 | label=""; 19 | docs_source_release_notes_______rst [label=< 20 | 21 | 22 | 23 | 24 |
3.7.2.rst
3.7.1 -- 2019-01-30
3.7.2 -- 2019-01-30
Bugs Fixed
> ] 25 | } 26 | subgraph cluster_docs_source_release_notes_______rst { 27 | style=dotted;penwidth=1;color=black;href="filename:docs/source/release-notes/3.7.3.rst:gitk:scroll_to_file {docs/source/release-notes/3.7.3.rst}" bgcolor="white"; label=< 28 |
3.7.3.rst
>; 29 | style=invis; 30 | label=""; 31 | docs_source_release_notes_______rst [label=< 32 | 33 | 34 | 35 |
3.7.3.rst
3.7.3 -- 2019-01-30
Bugs Fixed
> fillcolor="#aaffaa"] 36 | } 37 | subgraph cluster_docs_source_release_notes_index_rst { 38 | style=dotted;penwidth=1;color=black;href="filename:docs/source/release-notes/index.rst:gitk:scroll_to_file {docs/source/release-notes/index.rst}" bgcolor="white"; label=< 39 |
index.rst
>; 40 | style=invis; 41 | label=""; 42 | docs_source_release_notes_index_rst [label=< 43 | 44 | 45 | 46 | 47 | 48 |
index.rst
3.x Release Series
2.x Release Series
1.x Release Series
0.x Release Series
> ] 49 | } 50 | } 51 | subgraph cluster_p_src_flake_ { 52 | label=< 53 |
src/flake8
>; 54 | bgcolor="white"; color="#777777"; penwidth=5;subgraph cluster_src_flake____init___py { 55 | style=dotted;penwidth=1;color=black;href="filename:src/flake8/__init__.py:gitk:scroll_to_file {src/flake8/__init__.py}" bgcolor="white"; label=< 56 |
__init__.py
>; 57 | style=invis; 58 | label=""; 59 | src_flake____init___py [label=< 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
__init__.py
+LOG
#__version__
#__version_info__
#_EXTRA_VERBOSE
#_VERBOSITY_TO_LOG_LEVEL
+LOG_FORMAT
+configure_logging()
> ] 69 | } 70 | subgraph cluster_src_flake__style_guide_py { 71 | style=dotted;penwidth=1;color=black;href="filename:src/flake8/style_guide.py:gitk:scroll_to_file {src/flake8/style_guide.py}" bgcolor="white"; label=< 72 |
style_guide.py
>; 73 | src_flake__style_guide_py [label=< 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 |
style_guide.py
#__all__
+LOG
#_Violation
+lru_cache()
+find_noqa()
+find_more_specific()
+find_first_match()
> ] 83 | src_flake__style_guide_py_Selected [label=< 84 | 85 | 86 | 87 |
Selected
+Explicitly
+Implicitly
> ] 88 | src_flake__style_guide_py_Ignored [label=< 89 | 90 | 91 | 92 |
Ignored
+Explicitly
+Implicitly
> ] 93 | src_flake__style_guide_py_Decision [label=< 94 | 95 | 96 | 97 |
Decision
+Ignored
+Selected
> ] 98 | src_flake__style_guide_py_Violation [label=< 99 | 100 | 101 | 102 |
Violation
+is_inline_ignored()
+is_in()
> ] 103 | src_flake__style_guide_py_DecisionEngine [label=< 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 |
DecisionEngine
+__init__()
#_in_all_selected()
#_in_extended_selected()
+was_selected()
+was_ignored()
+more_specific_decision_for()
+make_decision()
+decision_for()
> ] 114 | src_flake__style_guide_py_StyleGuideManager [label=< 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 |
StyleGuideManager
+__init__()
+populate_style_guides_with()
+style_guide_for()
+processing_file()
+handle_error()
+add_diff_ranges()
> ] 123 | src_flake__style_guide_py_StyleGuide [label=< 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 |
StyleGuide
+__init__()
+__repr__()
+copy()
+processing_file()
+applies_to()
+should_report_error()
+handle_error()
+add_diff_ranges()
> ] 134 | } 135 | subgraph cluster_src_flake__utils_py { 136 | style=dotted;penwidth=1;color=black;href="filename:src/flake8/utils.py:gitk:scroll_to_file {src/flake8/utils.py}" bgcolor="white"; label=< 137 |
utils.py
>; 138 | style=invis; 139 | label=""; 140 | src_flake__utils_py [label=< 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 |
utils.py
+DIFF_HUNK_REGEXP
+COMMA_SEPARATED_LIST_RE
+LOCAL_PLUGIN_LIST_RE
#_Token
#_CODE
#_FILE
#_COLON
#_COMMA
#_WS
#_EOF
...
+fnmatch()
+parse_comma_separated_list()
+parse_files_to_codes_mapping()
+normalize_paths()
+normalize_path()
+stdin_get_value()
+parse_unified_diff()
+is_windows()
+can_run_multiprocessing_on_windows()
+is_using_stdin()
...
> ] 165 | } 166 | } 167 | subgraph p_src_flake__main { 168 | subgraph cluster_src_flake__main_application_py { 169 | style=dotted;penwidth=1;color=black;href="filename:src/flake8/main/application.py:gitk:scroll_to_file {src/flake8/main/application.py}" bgcolor="white"; label=< 170 |
src/flake8/main/application.py
>; 171 | src_flake__main_application_py [label=< 172 | 173 | 174 |
src/flake8/main/
application.py
+LOG
> ] 175 | src_flake__main_application_py_Application [label=< 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 |
Application
+__init__()
+parse_preliminary_options_and_args()
+exit()
+make_config_finder()
+find_plugins()
+register_plugin_options()
+parse_configuration_and_cli()
+formatter_for()
+make_formatter()
+make_guide()
...
> ] 189 | } 190 | } 191 | subgraph p_src_flake__plugins { 192 | subgraph cluster_src_flake__plugins_manager_py { 193 | style=dotted;penwidth=1;color=black;href="filename:src/flake8/plugins/manager.py:gitk:scroll_to_file {src/flake8/plugins/manager.py}" bgcolor="white"; label=< 194 |
src/flake8/plugins/manager.py
>; 195 | src_flake__plugins_manager_py [label=< 196 | 197 | 198 | 199 | 200 | 201 |
src/flake8/plugins/
manager.py
+LOG
#__all__
+NO_GROUP_FOUND
+version_for()
> ] 202 | src_flake__plugins_manager_py_Plugin [label=< 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 |
Plugin
+__init__()
+__repr__()
+to_dictionary()
+is_in_a_group()
+group()
+parameters()
+parameter_names()
+plugin()
+version()
+plugin_name()
...
> ] 216 | src_flake__plugins_manager_py_PluginManager [label=< 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 |
PluginManager
+__init__()
#_load_local_plugins()
#_load_entrypoint_plugins()
#_load_plugin_from_entrypoint()
+map()
+versions()
> ] 225 | src_flake__plugins_manager_py_PluginTypeManager [label=< 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 |
PluginTypeManager
+namespace
+__init__()
+__contains__()
+__getitem__()
+get()
+names()
+plugins()
#_generate_call_function()
+load_plugins()
+register_plugin_versions()
+register_options()
...
> ] 240 | src_flake__plugins_manager_py_Checkers [label=< 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 |
Checkers
+namespace
+checks_expecting()
+to_dictionary()
+register_options()
+ast_plugins()
+logical_line_plugins()
+physical_line_plugins()
> ] 250 | src_flake__plugins_manager_py_ReportFormatters [label=< 251 | 252 | 253 |
ReportFormatters
+namespace
> ] 254 | } 255 | } 256 | subgraph p_tests_integration { 257 | subgraph cluster_tests_integration_test_main_py { 258 | style=dotted;penwidth=1;color=black;href="filename:tests/integration/test_main.py:gitk:scroll_to_file {tests/integration/test_main.py}" bgcolor="white"; label=< 259 |
tests/integration/test_main.py
>; 260 | style=invis; 261 | label=""; 262 | tests_integration_test_main_py [label=< 263 | 264 | 265 | 266 | 267 |
tests/integration/
test_main.py
+test_diff_option()
+test_statistics_option()
+test_malformed_per_file_ignores_error()
> ] 268 | } 269 | } 270 | subgraph cluster_p_tests_unit { 271 | label=< 272 |
tests/unit
>; 273 | bgcolor="white"; color="#777777"; penwidth=5;subgraph cluster_tests_unit_test_style_guide_py { 274 | style=dotted;penwidth=1;color=black;href="filename:tests/unit/test_style_guide.py:gitk:scroll_to_file {tests/unit/test_style_guide.py}" bgcolor="white"; label=< 275 |
test_style_guide.py
>; 276 | style=invis; 277 | label=""; 278 | tests_unit_test_style_guide_py [label=< 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 |
test_style_guide.py
+PER_FILE_IGNORES_UNPARSED
+create_options()
+test_handle_error_does_not_raise_type_errors()
+test_style_guide_manager()
+test_style_guide_applies_to()
+test_style_guide_manager_pre_file_ignores_parsing()
+test_style_guide_manager_pre_file_ignores()
+test_style_guide_manager_style_guide_for()
> ] 289 | } 290 | subgraph cluster_tests_unit_test_utils_py { 291 | style=dotted;penwidth=1;color=black;href="filename:tests/unit/test_utils.py:gitk:scroll_to_file {tests/unit/test_utils.py}" bgcolor="white"; label=< 292 |
test_utils.py
>; 293 | style=invis; 294 | label=""; 295 | tests_unit_test_utils_py [label=< 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 |
test_utils.py
+RELATIVE_PATHS
+SINGLE_FILE_DIFF
+SINGLE_FILE_INFO
+TWO_FILE_DIFF
+TWO_FILE_INFO
+MULTI_FILE_DIFF
+MULTI_FILE_INFO
+test_parse_comma_separated_list()
+test_parse_files_to_codes_mapping()
+test_invalid_file_list()
+test_normalize_path()
+test_normalize_paths()
+test_is_windows_checks_for_nt()
+test_fnmatch()
+test_fnmatch_returns_the_default_with_empty_default()
+test_filenames_from_a_directory()
+test_filenames_from_a_directory_with_a_predicate()
...
> ] 316 | } 317 | } 318 | src_flake__main_application_py_Application -> src_flake__plugins_manager_py_Checkers [] 319 | src_flake__main_application_py_Application -> src_flake__plugins_manager_py_ReportFormatters [] 320 | src_flake__main_application_py_Application -> src_flake__style_guide_py_StyleGuideManager [] 321 | src_flake__main_application_py_Application -> src_flake__style_guide_py_StyleGuide [] 322 | src_flake__plugins_manager_py_PluginTypeManager -> src_flake__plugins_manager_py_Plugin [] 323 | src_flake__plugins_manager_py_PluginTypeManager -> src_flake__plugins_manager_py_PluginManager [] 324 | src_flake__style_guide_py_StyleGuide -> src_flake__style_guide_py_Violation [] 325 | src_flake__style_guide_py_StyleGuide -> src_flake__style_guide_py_DecisionEngine [] 326 | src_flake__utils_py -> src_flake__plugins_manager_py_Plugin [] 327 | tests_integration_test_main_py -> src_flake__main_application_py_Application [] 328 | tests_unit_test_style_guide_py -> src_flake__style_guide_py_StyleGuide [] 329 | tests_unit_test_style_guide_py -> src_flake__style_guide_py_StyleGuideManager [] 330 | tests_unit_test_utils_py -> src_flake__plugins_manager_py_Plugin [] 331 | edge [arrowhead=empty]; 332 | src_flake__plugins_manager_py_Checkers -> src_flake__plugins_manager_py_PluginTypeManager [] 333 | src_flake__plugins_manager_py_ReportFormatters -> src_flake__plugins_manager_py_PluginTypeManager [] 334 | } 335 | 336 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------