chocomint 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ff362c173d26ea61776f090575fc846b138ef7000ba0d9dd5df90adaa5fd3ef
4
- data.tar.gz: bea2b7dcb44f8f7d525ed46586576ab3a6fc38a6439ab58de91f3739561b671a
3
+ metadata.gz: c1e486a61190ec1d3548de09e65d74ea64d42a664c3d536c0e8013f61e6e2c1c
4
+ data.tar.gz: b7e23eedd909b6d2dffaeaa341721e7ccbd3bc91a2556a87d2654991c9475443
5
5
  SHA512:
6
- metadata.gz: 9945d52d514f88f02fe244c39915b52d8d50ee73e128416f93bab1db2f3a6a0f97c229e53fb28b79ccf6f66b5e7549e81f06fb102a9279d79d89bcea9a87273c
7
- data.tar.gz: c1ab4e9ae01521a6d384eaf682b46807f3f8bbaf93ffd7c9adbfc8f8d700b13779cb768482693d326921475b2c38143b615035729084e668d3e2e06a45268325
6
+ metadata.gz: 5234597d4d58068f04609037aaf53d058b48a0ddfea9a12240cc64c84ace0371b5277aee1442dcea8e1b8239449a7da0e32f976aca1672f4da3b5dfa90ee6645
7
+ data.tar.gz: e42a3f878d613ce09722db0929d745d3aaf3fdda93698eeb691203834063a9bf3090d10bf5aca20f7797391b76305885fcf89c63e6c03c67ae58580520a46704
@@ -28,8 +28,8 @@ module Chocomint
28
28
  def handle_edit_files(req, res)
29
29
  return edit_method_guard(res) unless req.request_method == "GET"
30
30
 
31
- entries = list_workspace_files
32
- edit_json(res, 200, "files" => entries, "dirs" => list_workspace_empty_dirs(entries),
31
+ files, empty_dirs = walk_workspace_tree
32
+ edit_json(res, 200, "files" => files, "dirs" => empty_dirs,
33
33
  "root" => relative_root, "abs_root" => edit_root.tr("\\", "/"))
34
34
  rescue Chocomint::Error => e
35
35
  edit_json(res, 500, "error" => e.message)
@@ -39,60 +39,60 @@ module Chocomint
39
39
  MAX_LISTED_FILES = 2000
40
40
 
41
41
  # 中身を展開しない VCS メタデータ・依存パッケージ等 (数千件で一覧を埋め尽くすため除外する)。
42
- EXCLUDED_TREE_DIRS = %w[.git .hg .svn .venv venv node_modules .bundle vendor/bundle].freeze
42
+ # ディレクトリ名 (basename) で判定するため、"vendor/bundle" のようなパス階層は含めない。
43
+ EXCLUDED_TREE_DIRS = %w[.git .hg .svn .venv venv node_modules .bundle].freeze
43
44
 
44
- def list_workspace_files
45
+ # workspace ツリーを1度だけ手動再帰し、[ファイル相対パス, 空ディレクトリ相対パス] を返す。
46
+ #
47
+ # Dir.glob("**/*") はツリー全体を舐めてから除外判定するため、node_modules を大量に
48
+ # 含むホームディレクトリ (例: C:\Users\<name>) を起点にすると応答が返らず WORKING
49
+ # DIRECTORY が空のままになる。除外ディレクトリはその配下へ降りる前に枝刈りし、
50
+ # MAX_LISTED_FILES 到達で打ち切ることで巨大ツリーでも実用的な時間で応答する。
51
+ def walk_workspace_tree
45
52
  root = edit_root
46
- return [] unless File.directory?(root)
53
+ return [[], []] unless File.directory?(root)
47
54
 
48
55
  files = []
49
- # File::FNM_DOTMATCH "." 始まりの隠しファイル/ディレクトリも列挙対象に含める。
50
- Dir.glob("**/*", File::FNM_DOTMATCH, base: root).each do |rel|
51
- rel = rel.tr("\\", "/")
52
- # "." / ".." など自己・親参照は除外する (FNM_DOTMATCH で拾われるため)。
53
- next if [".", ".."].include?(File.basename(rel))
54
- # .git などの配下は展開しない (メタデータで一覧が埋まるのを防ぐ)。
55
- next if EXCLUDED_TREE_DIRS.any? { |d| rel == d || rel.start_with?("#{d}/") }
56
-
57
- abs = File.join(root, rel)
58
- next unless File.file?(abs)
59
-
60
- files << rel
61
- break if files.size >= MAX_LISTED_FILES
62
- end
63
- files.sort
64
- end
56
+ # 中身を1件も持たないディレクトリ (files からツリー復元できない) を別途フロントに伝える。
57
+ empty_dirs = []
58
+ stack = [""] # root からの相対ディレクトリ (root 自身は "")
65
59
 
66
- # files に1件も含まれないディレクトリ (空ディレクトリ、または空ディレクトリのみを含むディレクトリ) を列挙する。
67
- # ファイル一覧からはツリー構築できないため、これらを別途フロントエンドに伝えてツリーに表示させる。
68
- def list_workspace_empty_dirs(files)
69
- root = edit_root
70
- return [] unless File.directory?(root)
71
-
72
- covered = files.each_with_object(Set.new) do |rel, set|
73
- parts = rel.split("/")
74
- parts.pop
75
- prefix = +""
76
- parts.each do |part|
77
- prefix = prefix.empty? ? part : "#{prefix}/#{part}"
78
- set << prefix
79
- end
80
- end
60
+ until stack.empty?
61
+ break if files.size >= MAX_LISTED_FILES
81
62
 
82
- dirs = []
83
- Dir.glob("**/*", File::FNM_DOTMATCH, base: root).each do |rel|
84
- rel = rel.tr("\\", "/")
85
- next if [".", ".."].include?(File.basename(rel))
86
- next if EXCLUDED_TREE_DIRS.any? { |d| rel == d || rel.start_with?("#{d}/") }
87
- next if covered.include?(rel)
63
+ rel_dir = stack.pop
64
+ abs_dir = rel_dir.empty? ? root : File.join(root, rel_dir)
88
65
 
89
- abs = File.join(root, rel)
90
- next unless File.directory?(abs)
66
+ child_count = 0
67
+ begin
68
+ Dir.each_child(abs_dir) do |name|
69
+ next if name == "." || name == ".."
70
+
71
+ rel = rel_dir.empty? ? name : "#{rel_dir}/#{name}"
72
+ abs = File.join(abs_dir, name)
73
+
74
+ if File.directory?(abs)
75
+ child_count += 1
76
+ # 除外ディレクトリは配下へ降りない (中身も一覧に出さない)。
77
+ next if EXCLUDED_TREE_DIRS.include?(name)
78
+
79
+ stack.push(rel)
80
+ elsif File.file?(abs)
81
+ child_count += 1
82
+ files << rel
83
+ break if files.size >= MAX_LISTED_FILES
84
+ end
85
+ end
86
+ rescue SystemCallError
87
+ # 権限不足などで開けないディレクトリはスキップする (ホーム配下で発生しうる)。
88
+ next
89
+ end
91
90
 
92
- dirs << rel
93
- break if dirs.size >= MAX_LISTED_FILES
91
+ # 子を1件も持たないディレクトリはツリーに現れないため、明示的に伝える (root は除く)。
92
+ empty_dirs << rel_dir if child_count.zero? && !rel_dir.empty?
94
93
  end
95
- dirs.sort
94
+
95
+ [files.sort, empty_dirs.sort]
96
96
  end
97
97
 
98
98
  # ---- ファイル取得 -------------------------------------------------------
data/lib/chocomint.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Chocomint
4
- VERSION = "1.0.1"
4
+ VERSION = "1.0.2"
5
5
  end
6
6
 
7
7
  require_relative "chocomint/errors"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chocomint
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - ひかり (Himeyama)