mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +00:00 
			
		
		
		
	lua: immediate-callback safe print()
This commit is contained in:
		@@ -323,6 +323,42 @@ void executor_exec_lua(const String str, typval_T *const ret_tv)
 | 
				
			|||||||
  nlua_pop_typval(lstate, ret_tv);
 | 
					  nlua_pop_typval(lstate, ret_tv);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void nlua_print_event(void **argv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  char *str = argv[0];
 | 
				
			||||||
 | 
					  const size_t len = (size_t)(intptr_t)argv[1]-1;  // exclude final NUL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (size_t i = 0; i < len;) {
 | 
				
			||||||
 | 
					    const size_t start = i;
 | 
				
			||||||
 | 
					    while (i < len) {
 | 
				
			||||||
 | 
					      switch (str[i]) {
 | 
				
			||||||
 | 
					        case NUL: {
 | 
				
			||||||
 | 
					          str[i] = NL;
 | 
				
			||||||
 | 
					          i++;
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        case NL: {
 | 
				
			||||||
 | 
					          // TODO(bfredl): use proper multiline msg? Probably should implement
 | 
				
			||||||
 | 
					          // print() in lua in terms of nvim_message(), when it is available.
 | 
				
			||||||
 | 
					          str[i] = NUL;
 | 
				
			||||||
 | 
					          i++;
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        default: {
 | 
				
			||||||
 | 
					          i++;
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    msg((char_u *)str + start);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (len && str[len - 1] == NUL) {  // Last was newline
 | 
				
			||||||
 | 
					    msg((char_u *)"");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  xfree(str);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Print as a Vim message
 | 
					/// Print as a Vim message
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// @param  lstate  Lua interpreter state.
 | 
					/// @param  lstate  Lua interpreter state.
 | 
				
			||||||
@@ -362,47 +398,24 @@ static int nlua_print(lua_State *const lstate)
 | 
				
			|||||||
    lua_pop(lstate, 1);
 | 
					    lua_pop(lstate, 1);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
#undef PRINT_ERROR
 | 
					#undef PRINT_ERROR
 | 
				
			||||||
  lua_pop(lstate, nargs + 1);
 | 
					 | 
				
			||||||
  ga_append(&msg_ga, NUL);
 | 
					  ga_append(&msg_ga, NUL);
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    const size_t len = (size_t)msg_ga.ga_len - 1;
 | 
					 | 
				
			||||||
    char *const str = (char *)msg_ga.ga_data;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (size_t i = 0; i < len;) {
 | 
					  if (in_fast_callback) {
 | 
				
			||||||
      const size_t start = i;
 | 
					    multiqueue_put(main_loop.events, nlua_print_event,
 | 
				
			||||||
      while (i < len) {
 | 
					                   2, msg_ga.ga_data, msg_ga.ga_len);
 | 
				
			||||||
        switch (str[i]) {
 | 
					  } else {
 | 
				
			||||||
          case NUL: {
 | 
					    nlua_print_event((void *[]){ msg_ga.ga_data,
 | 
				
			||||||
            str[i] = NL;
 | 
					                                 (void *)(intptr_t)msg_ga.ga_len });
 | 
				
			||||||
            i++;
 | 
					 | 
				
			||||||
            continue;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
          case NL: {
 | 
					 | 
				
			||||||
            str[i] = NUL;
 | 
					 | 
				
			||||||
            i++;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          default: {
 | 
					 | 
				
			||||||
            i++;
 | 
					 | 
				
			||||||
            continue;
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      msg((char_u *)str + start);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (len && str[len - 1] == NUL) {  // Last was newline
 | 
					 | 
				
			||||||
      msg((char_u *)"");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  ga_clear(&msg_ga);
 | 
					 | 
				
			||||||
  return 0;
 | 
					  return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
nlua_print_error:
 | 
					nlua_print_error:
 | 
				
			||||||
  emsgf(_("E5114: Error while converting print argument #%i: %.*s"),
 | 
					 | 
				
			||||||
        curargidx, (int)errmsg_len, errmsg);
 | 
					 | 
				
			||||||
  ga_clear(&msg_ga);
 | 
					  ga_clear(&msg_ga);
 | 
				
			||||||
  lua_pop(lstate, lua_gettop(lstate));
 | 
					  const char *fmt = _("E5114: Error while converting print argument #%i: %.*s");
 | 
				
			||||||
  return 0;
 | 
					  size_t len = (size_t)vim_snprintf((char *)IObuff, IOSIZE, fmt, curargidx,
 | 
				
			||||||
 | 
					                                    (int)errmsg_len, errmsg);
 | 
				
			||||||
 | 
					  lua_pushlstring(lstate, (char *)IObuff, len);
 | 
				
			||||||
 | 
					  return lua_error(lstate);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// debug.debug: interaction with user while debugging.
 | 
					/// debug.debug: interaction with user while debugging.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@ local command = helpers.command
 | 
				
			|||||||
local write_file = helpers.write_file
 | 
					local write_file = helpers.write_file
 | 
				
			||||||
local redir_exec = helpers.redir_exec
 | 
					local redir_exec = helpers.redir_exec
 | 
				
			||||||
local alter_slashes = helpers.alter_slashes
 | 
					local alter_slashes = helpers.alter_slashes
 | 
				
			||||||
 | 
					local exec_lua = helpers.exec_lua
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local screen
 | 
					local screen
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -53,11 +54,11 @@ describe('print', function()
 | 
				
			|||||||
      v_tblout = setmetatable({}, meta_tblout)
 | 
					      v_tblout = setmetatable({}, meta_tblout)
 | 
				
			||||||
    ]])
 | 
					    ]])
 | 
				
			||||||
    eq('', redir_exec('luafile ' .. fname))
 | 
					    eq('', redir_exec('luafile ' .. fname))
 | 
				
			||||||
    eq('\nE5114: Error while converting print argument #2: [NULL]',
 | 
					    eq('\nE5105: Error while calling lua chunk: E5114: Error while converting print argument #2: [NULL]',
 | 
				
			||||||
       redir_exec('lua print("foo", v_nilerr, "bar")'))
 | 
					       redir_exec('lua print("foo", v_nilerr, "bar")'))
 | 
				
			||||||
    eq('\nE5114: Error while converting print argument #2: Xtest-functional-lua-overrides-luafile:2: abc',
 | 
					    eq('\nE5105: Error while calling lua chunk: E5114: Error while converting print argument #2: Xtest-functional-lua-overrides-luafile:2: abc',
 | 
				
			||||||
       redir_exec('lua print("foo", v_abcerr, "bar")'))
 | 
					       redir_exec('lua print("foo", v_abcerr, "bar")'))
 | 
				
			||||||
    eq('\nE5114: Error while converting print argument #2: <Unknown error: lua_tolstring returned NULL for tostring result>',
 | 
					    eq('\nE5105: Error while calling lua chunk: E5114: Error while converting print argument #2: <Unknown error: lua_tolstring returned NULL for tostring result>',
 | 
				
			||||||
       redir_exec('lua print("foo", v_tblout, "bar")'))
 | 
					       redir_exec('lua print("foo", v_tblout, "bar")'))
 | 
				
			||||||
  end)
 | 
					  end)
 | 
				
			||||||
  it('prints strings with NULs and NLs correctly', function()
 | 
					  it('prints strings with NULs and NLs correctly', function()
 | 
				
			||||||
@@ -76,6 +77,29 @@ describe('print', function()
 | 
				
			|||||||
    eq('\nabc ', redir_exec('lua print("abc", "")'))
 | 
					    eq('\nabc ', redir_exec('lua print("abc", "")'))
 | 
				
			||||||
    eq('\nabc  def', redir_exec('lua print("abc", "", "def")'))
 | 
					    eq('\nabc  def', redir_exec('lua print("abc", "", "def")'))
 | 
				
			||||||
  end)
 | 
					  end)
 | 
				
			||||||
 | 
					  it('defers printing in luv event handlers', function()
 | 
				
			||||||
 | 
					    exec_lua([[
 | 
				
			||||||
 | 
					      local cmd = ...
 | 
				
			||||||
 | 
					      function test()
 | 
				
			||||||
 | 
					        local timer = vim.loop.new_timer()
 | 
				
			||||||
 | 
					        local done = false
 | 
				
			||||||
 | 
					        timer:start(10, 0, function()
 | 
				
			||||||
 | 
					          print("very fast")
 | 
				
			||||||
 | 
					          timer:close()
 | 
				
			||||||
 | 
					          done = true
 | 
				
			||||||
 | 
					        end)
 | 
				
			||||||
 | 
					        -- be kind to slow travis OS X jobs:
 | 
				
			||||||
 | 
					        -- loop until we know for sure the callback has been executed
 | 
				
			||||||
 | 
					        while not done do
 | 
				
			||||||
 | 
					          os.execute(cmd)
 | 
				
			||||||
 | 
					          vim.loop.run("nowait") -- fake os_breakcheck()
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        print("very slow")
 | 
				
			||||||
 | 
					        vim.api.nvim_command("sleep 1m") -- force deferred event processing
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    ]], (iswin() and "timeout 1") or "sleep 0.1")
 | 
				
			||||||
 | 
					    eq('\nvery slow\nvery fast',redir_exec('lua test()'))
 | 
				
			||||||
 | 
					  end)
 | 
				
			||||||
end)
 | 
					end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('debug.debug', function()
 | 
					describe('debug.debug', function()
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user