Skip to content

Commit 69e2bea

Browse files
committed
Add copy file command.
1 parent 450aaca commit 69e2bea

File tree

10 files changed

+223
-66
lines changed

10 files changed

+223
-66
lines changed

AdvancedNewFile.sublime-settings

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,5 +143,11 @@
143143
// cannot be resolved from the current active view. If an index outside of the range
144144
// of existing folders is used, it will default to 0 (the top level folder). If no
145145
// folders exist as part of the project the home directory will be used.
146-
"relative_fallback_index": 0
146+
"relative_fallback_index": 0,
147+
148+
// Setting to control if the extension will be automatically applied to renamed files.
149+
"append_extension_on_copy": true,
150+
151+
// Same as `rename_default`, but applied to copy command.
152+
"copy_default": ""
147153
}

Default.sublime-commands

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
{ "caption": "ANF: New File", "command": "advanced_new_file_new" },
33
{ "caption": "ANF: Rename File", "command": "advanced_new_file_move"},
44
{ "caption": "ANF: Delete File", "command": "advanced_new_file_delete"},
5-
{ "caption": "ANF: Delete Current File", "command": "advanced_new_file_delete", "args": {"current": true}}
5+
{ "caption": "ANF: Delete Current File", "command": "advanced_new_file_delete", "args": {"current": true}},
6+
{ "caption": "ANF: Copy Current File", "command": "advanced_new_file_copy" }
67
]

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ Setting to control if VCS management is used when moving and removing files.
160160

161161
An object containing information to use for templates when creating new files. The key values for this object should be a file extension. Files without extensions such as "Makefile" or ".bash_profile" use the full file name as the key. The value may either be a string of the content to be inserted or a list of paths. If a list of paths is specified, the name of the file will be displayed during selection. The paths must either be absolute, from the home directory of the user (`~/`), or relative to the Packages directory. These relative files should have the form "Packages/User/mytest.sublime-snippet". If a string is used, or the list contains a single entry, it will be automatically inserted into any newly created files.
162162

163-
`posix_input`:
163+
`shell_input`:
164164

165165
Setting this value to true will allow you to escape characters as you normally would when using a shell. For example, given the input string "foo\ bar", false would result in a file named " bar" in the foo directory. With the value set to true, a file named "foo bar" would be created. In addition, setting this value to true will allow for curly brace expansion. Currently, only comma separated entries are supported.
166166

@@ -172,6 +172,15 @@ Setting to control if the extension will be automatically applied to renamed fil
172172

173173
An integer value representing a folder index to be used when a relative path cannot be resolved from the current active view. If an index outside of the range of existing folders is used, it will default to 0 (the top level folder). If no folders exist as part of the project the home directory will be used.
174174

175+
`append_extension_on_copy`:
176+
177+
Setting to control if the extension will be automatically applied to copied files.
178+
179+
`copy_default`:
180+
181+
Same as `rename_default`, applied to copy command.
182+
183+
175184
### Project Specific Settings
176185
All of the above settings can also be specified as part of the project specific settings. These values override any previous values set by higher level settings, with aliases being an exception. Alias settings will be merged with higher level configurations for alias. In addition, if the same alias exist for both default/user settings and project settings, the project setting will take precedence.
177186

advanced_new_file/anf_util.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
SHELL_INPUT_SETTING = "shell_input"
3131
APPEND_EXTENSION_ON_MOVE_SETTING = "append_extension_on_move"
3232
RELATIVE_FALLBACK_INDEX_SETTING = "relative_fallback_index"
33+
APPEND_EXTENSION_ON_COPY_SETTING = "append_extension_on_copy"
34+
COPY_DEFAULT_SETTING = "copy_default"
3335

3436
SETTINGS = [
3537
ALIAS_SETTING,
@@ -59,7 +61,9 @@
5961
FILE_TEMPLATES_SETTING,
6062
SHELL_INPUT_SETTING,
6163
APPEND_EXTENSION_ON_MOVE_SETTING,
62-
RELATIVE_FALLBACK_INDEX_SETTING
64+
RELATIVE_FALLBACK_INDEX_SETTING,
65+
APPEND_EXTENSION_ON_COPY_SETTING,
66+
COPY_DEFAULT_SETTING
6367
]
6468

6569
NIX_ROOT_REGEX = r"^/"

advanced_new_file/commands/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from .new_file_command import AdvancedNewFileNew, AdvancedNewFileNewAtCommand, AdvancedNewFileNewEventListener
33
from .delete_file_command import AdvancedNewFileDelete
44
from .move_file_command import AdvancedNewFileMove, AdvancedNewFileMoveAtCommand
5+
from .copy_file_command import AdvancedNewFileCopy, AdvancedNewFileCopyAtCommand
56

67
__all__ = [
78
"AnfReplaceCommand",
@@ -11,5 +12,7 @@
1112
"AdvancedNewFileNewEventListener",
1213
"AdvancedNewFileMove",
1314
"AdvancedNewFileMoveAtCommand",
14-
"AdvancedNewFileDelete"
15+
"AdvancedNewFileDelete",
16+
"AdvancedNewFileCopy",
17+
"AdvancedNewFileCopyAtCommand"
1518
]

advanced_new_file/commands/command_base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,9 @@ def input_panel_caption(self):
231231
return ""
232232

233233
def show_filename_input(self, initial):
234+
caption = self.input_panel_caption()
234235
self.input_panel_view = self.window.show_input_panel(
235-
self.input_panel_caption(), initial,
236+
caption, initial,
236237
self.on_done, self.__update_filename_input, self.clear
237238
)
238239

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import os
2+
import re
3+
import shutil
4+
import sublime_plugin
5+
6+
from .duplicate_file_base import DuplicateFileBase
7+
from ..anf_util import *
8+
9+
class AdvancedNewFileCopy(DuplicateFileBase):
10+
def __init__(self, window):
11+
super(AdvancedNewFileCopy, self).__init__(window)
12+
13+
def get_default_setting(self):
14+
return COPY_DEFAULT_SETTING
15+
16+
def input_panel_caption(self):
17+
caption = 'Enter a new path to copy file'
18+
if self.is_python:
19+
caption = '%s (creates __init__.py in new dirs)' % caption
20+
return caption
21+
22+
def entered_file_action(self, path):
23+
attempt_copy = True
24+
path = self.try_append_extension(path)
25+
26+
directory = os.path.dirname(path)
27+
if not os.path.exists(directory):
28+
try:
29+
self.create_folder(directory)
30+
except OSError as e:
31+
attempt_copy = False
32+
sublime.error_message("Cannot create '" + path + "'." +
33+
" See console for details")
34+
print("Exception: %s '%s'" % (e.strerror, e.filename))
35+
36+
if attempt_copy:
37+
if self._copy_file(path):
38+
self.open_file(path)
39+
40+
def _copy_file(self, path):
41+
if os.path.isdir(path) or re.search(r"(/|\\)$", path):
42+
# use original name if a directory path has been passed in.
43+
path = os.path.join(path, self.original_name)
44+
45+
window = self.window
46+
copied = True
47+
if self.get_argument_name():
48+
self.copy_from_argument(path)
49+
elif self.view is not None:
50+
self.copy_from_view(self.view, path)
51+
else:
52+
copied = False
53+
sublime.error_message("Unable to copy file. No source file to move")
54+
55+
return copied
56+
57+
def copy_from_view(self, source_view, target):
58+
source = source_view.file_name()
59+
if source is None:
60+
self.copy_file_from_buffer(source_view, target)
61+
else:
62+
self.copy_file_from_disk(source, target)
63+
64+
def copy_file_from_buffer(self, source_view, target):
65+
content = self.view.substr(sublime.Region(0, self.view.size()))
66+
with open(target, "w") as file_obj:
67+
file_obj.write(content)
68+
69+
def copy_file_from_disk(self, source, target):
70+
self._copy_file_action(source, target)
71+
72+
def copy_from_argument(self, target):
73+
source_name = self.get_argument_name()
74+
file_view = self._find_open_file(source_name)
75+
76+
self._copy_file_action(source_name, target)
77+
78+
79+
def _copy_file_action(self, source, target):
80+
shutil.copy(source, target)
81+
82+
def get_status_prefix(self):
83+
return "Copying file to"
84+
85+
def get_append_extension_setting(self):
86+
return APPEND_EXTENSION_ON_COPY_SETTING
87+
88+
89+
class AdvancedNewFileCopyAtCommand(sublime_plugin.WindowCommand):
90+
def run(self, files):
91+
if len(files) != 1:
92+
return
93+
self.window.run_command("advanced_new_file_copy",
94+
{"rename_file": files[0]})
95+
96+
def is_visible(self, files):
97+
return len(files) == 1
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import os
2+
import re
3+
import sublime_plugin
4+
5+
from .command_base import AdvancedNewFileBase
6+
from ..anf_util import *
7+
8+
class DuplicateFileBase(AdvancedNewFileBase, sublime_plugin.WindowCommand):
9+
10+
def __init__(self, window):
11+
super(DuplicateFileBase, self).__init__(window)
12+
13+
def run(self, is_python=False, initial_path=None, rename_file=None):
14+
self.is_python = is_python
15+
self.run_setup()
16+
self.argument_name = rename_file
17+
18+
path = self.settings.get(self.get_default_setting(), "")
19+
current_file = self.view.file_name()
20+
if current_file:
21+
directory, current_file_name = os.path.split(current_file)
22+
path = path.replace("<filepath>", current_file)
23+
path = path.replace("<filedirectory>", directory + os.sep)
24+
else:
25+
current_file_name = ""
26+
27+
path = path.replace("<filename>", current_file_name)
28+
self.duplicate_setup()
29+
self.show_filename_input(
30+
path if len(path) > 0 else self.generate_initial_path())
31+
32+
def get_argument_name(self):
33+
return self.argument_name
34+
35+
def duplicate_setup(self):
36+
view = self.window.active_view()
37+
self.original_name = None
38+
if view is not None:
39+
view_file_name = view.file_name()
40+
if view_file_name:
41+
self.original_name = os.path.basename(view_file_name)
42+
43+
if self.original_name is None:
44+
self.original_name = ""
45+
46+
def update_status_message(self, creation_path):
47+
status_prefix = self.get_status_prefix()
48+
if self.is_copy_original_name(creation_path):
49+
creation_path = os.path.join(creation_path, self.original_name)
50+
else:
51+
creation_path = self.try_append_extension(creation_path)
52+
if self.view is not None:
53+
self.view.set_status("AdvancedNewFile", "%s %s " %
54+
(status_prefix, creation_path))
55+
else:
56+
sublime.status_message("%s %s" %
57+
(status_prefix, creation_path))
58+
59+
def is_copy_original_name(self, path):
60+
return (os.path.isdir(path) or
61+
os.path.basename(path) == "")
62+
63+
def try_append_extension(self, path):
64+
append_setting = self.get_append_extension_setting()
65+
print(append_setting)
66+
print(self.settings.get(append_setting))
67+
if self.settings.get(append_setting, False):
68+
if not self.is_copy_original_name(path):
69+
_, new_path_extension = os.path.splitext(path)
70+
if new_path_extension == "":
71+
argument_name = self.get_argument_name()
72+
if argument_name is None:
73+
_, extension = os.path.splitext(self.view.file_name())
74+
else:
75+
_, extension = os.path.splitext(argument_name)
76+
path += extension
77+
return path
78+
79+
80+

advanced_new_file/commands/move_file_command.py

Lines changed: 13 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -3,46 +3,20 @@
33
import shutil
44
import sublime_plugin
55

6-
from ..vcs.git.git_command_base import GitCommandBase
7-
from .command_base import AdvancedNewFileBase
6+
from .duplicate_file_base import DuplicateFileBase
87
from ..anf_util import *
8+
from ..vcs.git.git_command_base import GitCommandBase
99

1010

11-
class AdvancedNewFileMove(AdvancedNewFileBase, sublime_plugin.WindowCommand,
12-
GitCommandBase):
11+
class AdvancedNewFileMove(DuplicateFileBase, GitCommandBase):
1312
def __init__(self, window):
1413
super(AdvancedNewFileMove, self).__init__(window)
1514

16-
def run(self, is_python=False, initial_path=None, rename_file=None):
17-
self.is_python = is_python
18-
self.run_setup()
19-
self.rename_filename = rename_file
20-
21-
path = self.settings.get(RENAME_DEFAULT_SETTING)
22-
current_file = self.view.file_name()
23-
if current_file:
24-
directory, current_file_name = os.path.split(current_file)
25-
path = path.replace("<filepath>", current_file)
26-
path = path.replace("<filedirectory>", directory + os.sep)
27-
else:
28-
current_file_name = ""
29-
30-
path = path.replace("<filename>", current_file_name)
31-
self.show_filename_input(
32-
path if len(path) > 0 else self.generate_initial_path())
15+
def get_default_setting(self):
16+
return RENAME_DEFAULT_SETTING
3317

3418
def input_panel_caption(self):
3519
caption = 'Enter a new path for current file'
36-
view = self.window.active_view()
37-
self.original_name = None
38-
if view is not None:
39-
view_file_name = view.file_name()
40-
if view_file_name:
41-
self.original_name = os.path.basename(view_file_name)
42-
43-
if self.original_name is None:
44-
self.original_name = ""
45-
4620
if self.is_python:
4721
caption = '%s (creates __init__.py in new dirs)' % caption
4822
return caption
@@ -72,36 +46,24 @@ def entered_file_action(self, path):
7246
if attempt_open:
7347
self._rename_file(path)
7448

75-
def is_copy_original_name(self, path):
76-
return (os.path.isdir(path) or
77-
os.path.basename(path) == "")
78-
79-
def try_append_extension(self, path):
80-
if self.settings.get(APPEND_EXTENSION_ON_MOVE_SETTING, False):
81-
if not self.is_copy_original_name(path):
82-
_, new_path_extension = os.path.splitext(path)
83-
if new_path_extension == "":
84-
if self.rename_filename is None:
85-
_, extension = os.path.splitext(self.view.file_name())
86-
else:
87-
_, extension = os.path.splitext(self.rename_filename)
88-
path += extension
89-
return path
49+
def get_append_extension_setting(self):
50+
return APPEND_EXTENSION_ON_MOVE_SETTING
9051

9152
def _rename_file(self, file_path):
9253
if os.path.isdir(file_path) or re.search(r"(/|\\)$", file_path):
9354
# use original name if a directory path has been passed in.
9455
file_path = os.path.join(file_path, self.original_name)
9556

9657
window = self.window
97-
if self.rename_filename:
98-
file_view = self._find_open_file(self.rename_filename)
58+
rename_filename = self.get_argument_name()
59+
if rename_filename:
60+
file_view = self._find_open_file(rename_filename)
9961
if file_view is not None:
10062
self.view.run_command("save")
10163
window.focus_view(file_view)
10264
window.run_command("close")
10365

104-
self._move_action(self.rename_filename, file_path)
66+
self._move_action(rename_filename, file_path)
10567

10668
if file_view is not None:
10769
self.open_file(file_path)
@@ -131,16 +93,8 @@ def _move_action(self, from_file, to_file):
13193
else:
13294
shutil.move(from_file, to_file)
13395

134-
def update_status_message(self, creation_path):
135-
if self.is_copy_original_name(creation_path):
136-
creation_path = os.path.join(creation_path, self.original_name)
137-
else:
138-
creation_path = self.try_append_extension(creation_path)
139-
if self.view is not None:
140-
self.view.set_status("AdvancedNewFile", "Moving file to %s " %
141-
creation_path)
142-
else:
143-
sublime.status_message("Moving file to %s" % creation_path)
96+
def get_status_prefix(self):
97+
return "Moving file to"
14498

14599

146100
class AdvancedNewFileMoveAtCommand(sublime_plugin.WindowCommand):

0 commit comments

Comments
 (0)