Fix opening files that have special chars like '$' in filename

This fixes a whole host of related issues in MacVim that fails to
properly sanitize filenames when opening files using the GUI (e.g.
drag-and-drop, file type associations, etc). The core issue came from
the fact that the code used to rely on constructing Vimscript commands
to send to code Vim to parse using `addInput` (e.g. `:tabe <filename>`).
This was convenient and fast to add new feature but requires properly
sanitizing filenames (since that's an arbitrary user input). It used a
bespoke function `stringByEscapingSpecialFilenameCharacters` but it
didn't fully work ('$' would not be escaped so it would be interpreted
as a Vim variable) for all filenames.

One solution should be to use Vim's fnameescape() utility, but it
doesn't fully solve all edge cases. For example, if we hae a newline in
a filename (which is admittedly weird) it will still break the Vimscript
parsing.

Better solution is to simply construct the raw Ex commands in C
manually. It forces more tie-in to Vim's internals but is the most
robust way to arbitary filenames. Replace all usage of
stringByEscapingSpecialFilenameCharacters with this method:

* `handleOpenWithArguments:` is used by drag-and-drop and opening files
from GUI, so fix that to construct Vim function calls. Also fixes some
weird bugs in existing behavior (e.g. if you drag multiple files in but
one of them is opened alrady, it used to choose the wrong tab to do the
`sall` split in. Now just make sure to make a new tab).

* Dragging files to tab bars. MacVim has a special behavior where
dragging files to tab bars will open files in that particular tab.
Consolidate the behavior to use `handleOpenWithArguments` as well. Also
fix an issue where previously it would forcefully stomp whatever buffer
you had in that tab even if it had unsaved changes.

* Misc cases: New File Here macOS service and a "open first file if
exists already" functionality also need to be fixed. For these two
cases, make new MMBackend handlers just for simplicity as they aren't
doing anything complicated.

For future TODO:
- Implement tests for this when new testing infrastructure is up for
testing MacVim-specific functionality.

Fix #863.
This commit is contained in:
Yee Cheng Chin
2019-05-22 03:57:30 -07:00
parent 651ad8e80c
commit 4ef03eda26
7 changed files with 399 additions and 122 deletions
+9 -20
View File
@@ -976,21 +976,11 @@ fsEventCallback(ConstFSEventStreamRef streamRef,
//
// NOTE: Raise window before passing arguments, otherwise the
// selection will be lost when selectionRange is set.
firstFile = [firstFile stringByEscapingSpecialFilenameCharacters];
NSString *bufCmd = @"tab sb";
switch (layout) {
case MMLayoutHorizontalSplit: bufCmd = @"sb"; break;
case MMLayoutVerticalSplit: bufCmd = @"vert sb"; break;
case MMLayoutArglist: bufCmd = @"b"; break;
}
NSString *input = [NSString stringWithFormat:@"<C-\\><C-N>"
":let oldswb=&swb|let &swb=\"useopen,usetab\"|"
"%@ %@|let &swb=oldswb|unl oldswb|"
"cal foreground()<CR>", bufCmd, firstFile];
[vc addVimInput:input];
NSDictionary *args = [NSDictionary dictionaryWithObjectsAndKeys:
firstFile, @"filename",
[NSNumber numberWithInt:layout], @"layout",
nil];
[vc sendMessage:SelectAndFocusOpenedFileMsgID data:[args dictionaryAsData]];
}
[vc passArguments:arguments];
@@ -1465,16 +1455,15 @@ fsEventCallback(ConstFSEventStreamRef streamRef,
if (!dirIndicator)
path = [path stringByDeletingLastPathComponent];
path = [path stringByEscapingSpecialFilenameCharacters];
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
BOOL openInCurrentWindow = [ud boolForKey:MMOpenInCurrentWindowKey];
MMVimController *vc;
if (openInCurrentWindow && (vc = [self topmostVimController])) {
NSString *input = [NSString stringWithFormat:@"<C-\\><C-N>"
":tabe|cd %@<CR>", path];
[vc addVimInput:input];
NSDictionary *args = [NSDictionary dictionaryWithObjectsAndKeys:
path, @"path",
nil];
[vc sendMessage:NewFileHereMsgID data:[args dictionaryAsData]];
} else {
[self launchVimProcessWithArguments:nil workingDirectory:path];
}
+353 -46
View File
@@ -189,6 +189,8 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
- (void)startOdbEditWithArguments:(NSDictionary *)args;
- (void)handleXcodeMod:(NSData *)data;
- (void)handleOpenWithArguments:(NSDictionary *)args;
- (void)handleSelectAndFocusOpenedFile:(NSDictionary *)args;
- (void)handleNewFileHere:(NSDictionary *)args;
- (int)checkForModifiedBuffers;
- (void)addInput:(NSString *)input;
- (void)redrawScreen;
@@ -2075,6 +2077,10 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
[self handleXcodeMod:data];
} else if (OpenWithArgumentsMsgID == msgid) {
[self handleOpenWithArguments:[NSDictionary dictionaryWithData:data]];
} else if (SelectAndFocusOpenedFileMsgID == msgid) {
[self handleSelectAndFocusOpenedFile:[NSDictionary dictionaryWithData:data]];
} else if (NewFileHereMsgID == msgid) {
[self handleNewFileHere:[NSDictionary dictionaryWithData:data]];
} else if (FindReplaceMsgID == msgid) {
[self handleFindReplace:[NSDictionary dictionaryWithData:data]];
} else if (UseSelectionForFindMsgID == msgid) {
@@ -2637,6 +2643,7 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
// filenames list of filenames
// dontOpen don't open files specified in above argument
// layout which layout to use to open files
// tabpage a tab page to enter first before opening
// selectionRange range of characters to select
// searchText string to search for
// cursorLine line to position the cursor on
@@ -2653,6 +2660,7 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
int i, numFiles = filenames ? [filenames count] : 0;
BOOL openFiles = ![[args objectForKey:@"dontOpen"] boolValue];
int layout = [[args objectForKey:@"layout"] intValue];
int tabpage = [[args objectForKey:@"tabpage"] intValue];
if (starting > 0) {
// When Vim is starting we simply add the files to be opened to the
@@ -2694,6 +2702,11 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
//
// TODO: Figure out a better way to handle this?
if (openFiles && numFiles > 0) {
// Caller specified a tab page to enter first.
if (tabpage > 0) {
goto_tabpage(tabpage);
}
BOOL oneWindowInTab = topframe ? YES
: (topframe->fr_layout == FR_LEAF);
BOOL bufChanged = NO;
@@ -2707,85 +2720,236 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
// potentially cause multiple redraws.
flushDisabled = YES;
BOOL onlyOneTab = (first_tabpage->tp_next == NULL);
if (WIN_TABS == layout && !onlyOneTab) {
// By going to the last tabpage we ensure that the new tabs
// will appear last (if this call is left out, the taborder
// becomes messy).
goto_tabpage(9999);
}
// Make sure we're in normal mode first.
// TODO: The mixing of addInput and Ex commands is a little
// problematic because addInput is asynchronous and will therefore
// run after all the Ex commands. Should fix this in the future.
[self addInput:@"<C-\\><C-N>"];
// Note: We call Ex functions directly to edit files instead of
// using addInput. The reason is that addInput relies on us manually
// constructing an Ex command string which requires us to manually
// escape file names with special characters like "$". In Vim there
// isn't a good way to escape characters like "\n" even if we use
// fnameescape, and the injected newline could be used by a
// malicious actor to craft a file name of the form "someFile\n:q"
// (\n represents the real newline character, not '\' and 'n') where
// the :q will be interpreted as the next Ex command by addInput.
// This could mean dragging a file with malicious filename could
// execute arbitrary Ex cmds which is obviously not good.
//
// To avoid mistakes, just call the C functions directly which
// avoids the problem of command ambiguity in crafting and escaping
// the Ex cmd string to send over.
exarg_T ea;
vim_memset(&ea, 0, sizeof(ea));
if (numFiles > 1) {
cmdmod_T save_cmdmod = cmdmod;
vim_memset(&cmdmod, 0, sizeof(cmdmod));
BOOL splitInNewTab = NO;
// With "split layout" we open a new tab before opening
// multiple files if the current tab has more than one window
// or if there is exactly one window but whose buffer has a
// filename. (The :drop command ensures modified buffers get
// their own window.)
if ((WIN_HOR == layout || WIN_VER == layout) &&
(!oneWindowInTab || bufHasFilename))
[self addInput:@":tabnew<CR>"];
(!oneWindowInTab || bufHasFilename)) {
splitInNewTab = YES;
// The files are opened by constructing a ":drop ..." command
// and executing it.
NSMutableString *cmd = (WIN_TABS == layout)
? [NSMutableString stringWithString:@":tab drop"]
: [NSMutableString stringWithString:@":drop"];
char_u tabnewArgs[] = "";
char_u tabnewCmd[] = "tabnew";
for (i = 0; i < numFiles; ++i) {
NSString *file = [filenames objectAtIndex:i];
file = [file stringByEscapingSpecialFilenameCharacters];
[cmd appendString:@" "];
[cmd appendString:file];
// :tabnew
ea.arg = tabnewArgs;
ea.cmdidx = CMD_tabnew;
ea.cmd = tabnewCmd;
ex_splitview(&ea);
}
// Temporarily clear 'suffixes' so that the files are opened in
// the same order as they appear in the "filenames" array.
[self addInput:@":let mvim_oldsu=&su|set su=<CR>"];
//
// :let mvim_oldsu=&su|set su=
char_u *orig_p_su = p_su;
char_u empty_p_su[] = "";
p_su = empty_p_su;
[self addInput:cmd];
// The files are opened by constructing a ":drop ..." command
// and executing it. The "drop" commands take in a single
// argument with all the filenames in them, and the filenames
// are required to be properly escaped or else names like
// "$filename" will get substituted as a variable which is wrong.
garray_T filename_array;
ga_init2(&filename_array, sizeof(char_u*), numFiles);
for (i = 0; i < numFiles; ++i) {
NSString *file = [filenames objectAtIndex:i];
// Escape each file name and add to the growing array.
char_u *escapedFname = vim_strsave_fnameescape((char_u*)[file UTF8String], FALSE);
ga_add_string(&filename_array, escapedFname);
vim_free(escapedFname);
}
char_u* escapedFilenameList = ga_concat_strings(&filename_array, " ");
ga_clear_strings(&filename_array);
if (WIN_TABS == layout) {
tabpage_T *tp;
int numTabs = 0;
FOR_ALL_TABPAGES(tp) {
numTabs += 1;
}
// Convert ":drop ..." to ":$tab drop ..."
cmdmod.tab = numTabs + 1;
}
if (splitInNewTab) {
// If we are making a new tab to do a full split, don't
// bother using the :drop command which will switch to an
// open buffer if the first file is opened already, which
// we don't want. We just want a full tab with all the
// files split. Just set the arglist and the later :sall
// command will take care of it.
set_arglist(escapedFilenameList);
}
else {
char_u dropCmdname[] = "drop";
// :drop ... / :$tab drop ...
ea.arg = escapedFilenameList;
ea.cmd = dropCmdname;
ea.cmdidx = CMD_drop;
ex_drop(&ea);
}
// Clean up temporary strings.
vim_free(escapedFilenameList);
escapedFilenameList = NULL;
// Split the view into multiple windows if requested.
if (WIN_HOR == layout)
[self addInput:@"|sall"];
else if (WIN_VER == layout)
[self addInput:@"|vert sall"];
if (WIN_HOR == layout || WIN_VER == layout) {
vim_memset(&cmdmod, 0, sizeof(cmdmod));
if (WIN_VER == layout) {
// Convert :sall to :vert sall
cmdmod.split |= WSP_VERT;
}
char_u sallArg[] = "";
char_u sallCmdname[] = "sall";
// :sall / :vert sall
ea.arg = sallArg;
ea.cmd = sallCmdname;
ea.cmdidx = CMD_sall;
ex_all(&ea);
}
// Restore the global cmdmod.
cmdmod = save_cmdmod;
// Restore the old value of 'suffixes'.
[self addInput:@"|let &su=mvim_oldsu|unlet mvim_oldsu<CR>"];
} else {
p_su = orig_p_su;
} else { // numFiles == 1
typedef void (*ex_func_T) (exarg_T *eap);
ex_func_T exfunc = NULL;
char *cmdname = NULL;
cmdmod_T save_cmdmod = cmdmod;
vim_memset(&cmdmod, 0, sizeof(cmdmod));
// When opening one file we try to reuse the current window,
// but not if its buffer is modified or has a filename.
// However, the 'arglist' layout always opens the file in the
// current window.
NSString *file = [[filenames lastObject]
stringByEscapingSpecialFilenameCharacters];
NSString *cmd;
if (WIN_HOR == layout) {
if (!(bufHasFilename || bufChanged))
cmd = [NSString stringWithFormat:@":e %@", file];
else
cmd = [NSString stringWithFormat:@":sp %@", file];
if (!(bufHasFilename || bufChanged)) {
// :e <filename>
ea.cmdidx = CMD_edit;
exfunc = ex_edit;
cmdname = "edit";
} else {
// :sp <filename>
ea.cmdidx = CMD_split;
exfunc = ex_splitview;
cmdname = "spit";
}
} else if (WIN_VER == layout) {
if (!(bufHasFilename || bufChanged))
cmd = [NSString stringWithFormat:@":e %@", file];
else
cmd = [NSString stringWithFormat:@":vsp %@", file];
if (!(bufHasFilename || bufChanged)) {
// :e <filename>
ea.cmdidx = CMD_edit;
exfunc = ex_edit;
cmdname = "edit";
} else {
// :vsp <filename>
ea.cmdidx = CMD_vsplit;
exfunc = ex_splitview;
cmdname = "vsplit";
}
} else if (WIN_TABS == layout) {
if (oneWindowInTab && !(bufHasFilename || bufChanged))
cmd = [NSString stringWithFormat:@":e %@", file];
else
cmd = [NSString stringWithFormat:@":tabe %@", file];
if (oneWindowInTab && !(bufHasFilename || bufChanged)) {
// :e <filename>
ea.cmdidx = CMD_edit;
exfunc = ex_edit;
cmdname = "edit";
} else {
// :$tabedit <filename>
ea.cmdidx = CMD_tabedit;
exfunc = ex_splitview;
cmdname = "tabedit";
tabpage_T *tp;
int numTabs = 0;
FOR_ALL_TABPAGES(tp) {
numTabs += 1;
}
cmdmod.tab = numTabs + 1;
}
} else {
// (The :drop command will split if there is a modified
// buffer.)
cmd = [NSString stringWithFormat:@":drop %@", file];
// :drop <filename>
ea.cmdidx = CMD_drop;
exfunc = ex_drop;
cmdname = "drop";
}
[self addInput:cmd];
[self addInput:@"<CR>"];
char_u *cmdnameSaved = vim_strsave((char_u*)cmdname);
ea.cmd = cmdnameSaved;
NSString *file = [filenames lastObject];
char_u *filename = [file vimStringSave];
ea.arg = filename;
char_u *filenameEscaped = NULL;
if (ea.cmdidx == CMD_drop) {
// :drop works differently internally and we need to escape
// filenames first. Otherwise names like $filename will get
// substituted.
filenameEscaped = vim_strsave_fnameescape(filename, FALSE);
ea.arg = filenameEscaped;
}
if (exfunc != NULL) {
// Execute the Ex command that we have prepared. See notes
// about for why we do this instead of using addInput for
// processing file names.
exfunc(&ea);
}
cmdmod = save_cmdmod;
// Clean up temporary strings.
if (filenameEscaped) {
vim_free(filenameEscaped);
}
vim_free(filename);
vim_free(cmdnameSaved);
}
// Force screen redraw (does it have to be this complicated?).
@@ -2848,6 +3012,149 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
}
}
// This will select the window/tab of a particular window and bring the window
// to focus. If the buffer is hidden, it will make a new tab/split to show it.
//
// It basically does the equivalent of the following:
// let oldswb=&swb | let &swb="useopen,usetab"
// buffer <filename>
// let &swb=oldswb |unl oldswb
// call foreground()
- (void)handleSelectAndFocusOpenedFile:(NSDictionary *)args
{
// ARGUMENT: DESCRIPTION:
// -------------------------------------------------------------
// filename filename of the opened file to focus
// layout which layout to use to open files
ASLogDebug(@"args=%@", args);
NSString *filename = [args objectForKey:@"filename"];
char_u *filename_vim = [filename vimStringSave];
int layout = [[args objectForKey:@"layout"] intValue];
// Does the following the make sure the buffer command will always go to an
// already opened buffer:
// :let oldswb=&swb|let &swb="useopen,usetab"
unsigned orig_swb_flags = swb_flags;
swb_flags = SWB_USEOPEN | SWB_USETAB;
// Need to first manually find the bufnr first as the ex_buffer() command
// already expects that to be provided.
int bufnr = buflist_findpat(
filename_vim, filename_vim + STRLEN(filename_vim), FALSE, FALSE, FALSE);
if (bufnr >= 0)
{
// :sb / :vert sb / :tab sb / :b <filename> (depending on layout)
// The layout will only matter if the buffer is hidden and needs to be
// opened in a new window.
exarg_T ea;
vim_memset(&ea, 0, sizeof(ea));
cmdmod_T save_cmdmod = cmdmod;
vim_memset(&cmdmod, 0, sizeof(cmdmod));
char_u bufferCmd[] = "buffer";
char_u sbufferCmd[] = "sbuffer";
if (WIN_HOR == layout) {
// :sb <filename>
ea.cmdidx = CMD_sbuffer;
ea.cmd = sbufferCmd;
} else if (WIN_VER == layout) {
// :vert sb <filename>
ea.cmdidx = CMD_sbuffer;
ea.cmd = sbufferCmd;
cmdmod.split |= WSP_VERT;
} else if (WIN_TABS == layout) {
// :tab sb <filename>
ea.cmdidx = CMD_sbuffer;
ea.cmd = sbufferCmd;
tabpage_T *tp;
int numTabs = 0;
FOR_ALL_TABPAGES(tp) {
numTabs += 1;
}
cmdmod.tab = numTabs + 1;
} else {
// :b <filename>
ea.cmdidx = CMD_buffer;
ea.cmd = bufferCmd;
}
ea.arg = (char_u *)"";
ea.addr_count = 1;
ea.line2 = bufnr;
goto_buffer(&ea, DOBUF_FIRST, FORWARD, (int)ea.line2);
cmdmod = save_cmdmod;
}
// Restore the old value of 'switchbuf' and command modifiers
swb_flags = orig_swb_flags;
// Same as :call foreground()
[self activate];
vim_free(filename_vim);
// Force screen redraw (see handleOpenWithArguments:).
update_screen(NOT_VALID);
setcursor();
out_flush();
gui_update_cursor(FALSE, FALSE);
maketitle();
}
// This handles the "New File" action. It basically does the following to set up
// a new buffer in the provided directory:
// :tabnew | :tcd <path>
//
// It uses :tcd as this way the whole tab gets the specified path as a basis
// without unncessarily using a global :cd which messes with the other files
// this instance was already editing.
- (void)handleNewFileHere:(NSDictionary *)args
{
// ARGUMENT: DESCRIPTION:
// -------------------------------------------------------------
// path path to make a new buffer
NSString *path = [args objectForKey:@"path"];
char_u *path_vim = [path vimStringSave];
ASLogDebug(@"path=%s", path_vim);
exarg_T ea;
vim_memset(&ea, 0, sizeof(ea));
// :tabnew
char_u tabnewArgs[] = "";
char_u tabnewCmd[] = "tabnew";
ea.arg = tabnewArgs;
ea.cmdidx = CMD_tabnew;
ea.cmd = tabnewCmd;
ex_splitview(&ea);
// :tcd <path>
char_u tcdCmd[] = "tcd";
ea.arg = path_vim;
ea.cmdidx = CMD_tcd;
ea.cmd = tabnewCmd;
ex_cd(&ea);
vim_free(path_vim);
// Force screen redraw (see handleOpenWithArguments:).
update_screen(NOT_VALID);
setcursor();
out_flush();
gui_update_cursor(FALSE, FALSE);
maketitle();
}
- (int)checkForModifiedBuffers
{
// Return 1 if current buffer is modified, -1 if other buffer is modified,
+33 -16
View File
@@ -296,33 +296,50 @@ static BOOL isUnsafeMessage(int msgid);
noteNewRecentFilePaths:filenames];
}
// This is called when a file is dragged on top of a tab. We will open the file
// list similar to drag-and-dropped files.
- (void)file:(NSString *)filename draggedToTabAtIndex:(NSUInteger)tabIndex
{
filename = normalizeFilename(filename);
ASLogInfo(@"filename=%@ index=%ld", filename, tabIndex);
NSString *fnEsc = [filename stringByEscapingSpecialFilenameCharacters];
NSString *input = [NSString stringWithFormat:@"<C-\\><C-N>:silent "
"tabnext %ld |"
"edit! %@<CR>", tabIndex + 1, fnEsc];
[self addVimInput:input];
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
// This is similar to dropFiles:forceOpen: except we first switch to the
// selected tab, and just open the first file (this could be modified in the
// future to support multiple files). It also forces layout to be splits
// because we specified one tab to receive the file so doesn't make sense to
// open another tab.
int layout = MMLayoutHorizontalSplit;
if ([ud boolForKey:MMVerticalSplitKey])
layout = MMLayoutVerticalSplit;
NSDictionary *args = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:layout], @"layout",
@[filename], @"filenames",
[NSNumber numberWithInt:tabIndex + 1], @"tabpage",
nil];
[self sendMessage:OpenWithArgumentsMsgID data:[args dictionaryAsData]];
}
// This is called when a file is dragged on top of the tab bar but not a
// particular tab (e.g. the new tab button). We will open the file list similar
// to drag-and-dropped files.
- (void)filesDraggedToTabBar:(NSArray *)filenames
{
filenames = normalizeFilenames(filenames);
ASLogInfo(@"%@", filenames);
NSUInteger i, count = [filenames count];
NSMutableString *input = [NSMutableString stringWithString:@"<C-\\><C-N>"
":silent! tabnext 9999"];
for (i = 0; i < count; i++) {
NSString *fn = [filenames objectAtIndex:i];
NSString *fnEsc = [fn stringByEscapingSpecialFilenameCharacters];
[input appendFormat:@"|tabedit %@", fnEsc];
}
[input appendString:@"<CR>"];
[self addVimInput:input];
// This is similar to dropFiles:forceOpen: except we just force layout to be
// tabs (since the receipient is the tab bar, we assume that's the
// intention) instead of loading from user defaults.
int layout = MMLayoutTabs;
NSDictionary *args = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:layout], @"layout",
filenames, @"filenames",
nil];
[self sendMessage:OpenWithArgumentsMsgID data:[args dictionaryAsData]];
}
- (void)dropString:(NSString *)string
+2 -1
View File
@@ -236,6 +236,8 @@ extern const char * const MMVimMsgIDStrings[];
MSG(SetVimStateMsgID) \
MSG(SetDocumentFilenameMsgID) \
MSG(OpenWithArgumentsMsgID) \
MSG(SelectAndFocusOpenedFileMsgID) \
MSG(NewFileHereMsgID) \
MSG(CloseWindowMsgID) \
MSG(SetFullScreenColorMsgID) \
MSG(ShowFindReplaceDialogMsgID) \
@@ -353,7 +355,6 @@ extern NSString *VimFindPboardType;
@interface NSString (MMExtras)
- (NSString *)stringByEscapingSpecialFilenameCharacters;
- (NSString *)stringByRemovingFindPatterns;
- (NSString *)stringBySanitizingSpotlightSearch;
@end
-37
View File
@@ -88,43 +88,6 @@ debugStringForMessageQueue(NSArray *queue)
@implementation NSString (MMExtras)
- (NSString *)stringByEscapingSpecialFilenameCharacters
{
// NOTE: This code assumes that no characters already have been escaped.
NSMutableString *string = [self mutableCopy];
[string replaceOccurrencesOfString:@"\\"
withString:@"\\\\"
options:NSLiteralSearch
range:NSMakeRange(0, [string length])];
[string replaceOccurrencesOfString:@" "
withString:@"\\ "
options:NSLiteralSearch
range:NSMakeRange(0, [string length])];
[string replaceOccurrencesOfString:@"\t"
withString:@"\\\t "
options:NSLiteralSearch
range:NSMakeRange(0, [string length])];
[string replaceOccurrencesOfString:@"%"
withString:@"\\%"
options:NSLiteralSearch
range:NSMakeRange(0, [string length])];
[string replaceOccurrencesOfString:@"#"
withString:@"\\#"
options:NSLiteralSearch
range:NSMakeRange(0, [string length])];
[string replaceOccurrencesOfString:@"|"
withString:@"\\|"
options:NSLiteralSearch
range:NSMakeRange(0, [string length])];
[string replaceOccurrencesOfString:@"\""
withString:@"\\\""
options:NSLiteralSearch
range:NSMakeRange(0, [string length])];
return [string autorelease];
}
- (NSString *)stringByRemovingFindPatterns
{
// Remove some common patterns added to search strings that other apps are
+1 -2
View File
@@ -131,7 +131,6 @@ static void ex_mode(exarg_T *eap);
static void ex_wrongmodifier(exarg_T *eap);
static void ex_find(exarg_T *eap);
static void ex_open(exarg_T *eap);
static void ex_edit(exarg_T *eap);
#ifndef FEAT_GUI
# define ex_gui ex_nogui
static void ex_nogui(exarg_T *eap);
@@ -7138,7 +7137,7 @@ ex_open(exarg_T *eap)
/*
* ":edit", ":badd", ":visual".
*/
static void
void
ex_edit(exarg_T *eap)
{
do_exedit(eap, NULL);
+1
View File
@@ -33,6 +33,7 @@ void alist_expand(int *fnum_list, int fnum_len);
void alist_set(alist_T *al, int count, char_u **files, int use_curbuf, int *fnum_list, int fnum_len);
void alist_add(alist_T *al, char_u *fname, int set_fnum);
void alist_slash_adjust(void);
void ex_edit(exarg_T *eap);
void ex_splitview(exarg_T *eap);
void tabpage_new(void);
void do_exedit(exarg_T *eap, win_T *old_curwin);